libs: vendor qca (Qt Cryptographic Architecture, Qt6) for kf6-kwallet ksecretd
redbear-ci / check (push) Waiting to run
kf6-kwallet's ksecretd daemon (the org.freedesktop.secrets Secret Service provider) requires Qca-qt6 2.3.1. Vendor QCA 2.3.10 under the full-fork model and build the gcrypt provider plugin (backed by the vendored libgcrypt) so QCA has a working SymmetricCipher/Hash/PBKDF2 backend on Redox. External-toolkit providers (ossl/nss/botan/gnupg/pkcs11/cyrus-sasl) are disabled — those libraries are not part of the Redox userland; logger + softstore (pure-Qt) are kept. Source edit: the top-level Qt6 find_package required the Test component unconditionally; gate it on BUILD_TESTS so a tools/tests-off build configures without Qt6Test. Install via DESTDIR since QCA anchors its install dirs to the absolute Qt prefix (/usr), which ignores cmake --install --prefix. Build libgpg-error + libgcrypt with --with-pic so their static archives can be linked into the qca-gcrypt shared plugin. Add qca to kf6-kwallet deps.
@@ -26,6 +26,7 @@ dependencies = [
|
||||
"kf6-kwindowsystem",
|
||||
"kf6-ki18n",
|
||||
"libgcrypt",
|
||||
"qca",
|
||||
]
|
||||
script = """
|
||||
DYNAMIC_INIT
|
||||
|
||||
@@ -23,7 +23,7 @@ DYNAMIC_INIT
|
||||
# discovers the sysroot libgpg-error rather than any host copy.
|
||||
export GPGRT_CONFIG="${COOKBOOK_SYSROOT}/usr/bin/gpgrt-config"
|
||||
|
||||
cookbook_configure
|
||||
cookbook_configure --with-pic
|
||||
"""
|
||||
|
||||
[package]
|
||||
|
||||
@@ -45,7 +45,7 @@ typedef struct
|
||||
##
|
||||
EOF
|
||||
|
||||
cookbook_configure --enable-threads=posix
|
||||
cookbook_configure --enable-threads=posix --with-pic
|
||||
"""
|
||||
|
||||
[package]
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# qca (Qt Cryptographic Architecture, Qt6 build) — Qca-qt6.
|
||||
#
|
||||
# Required by kf6-kwallet's ksecretd daemon (the org.freedesktop.secrets Secret
|
||||
# Service provider): find_package(Qca-qt6 REQUIRED 2.3.1). QCA is a crypto
|
||||
# abstraction that loads a provider plugin at runtime; we build the gcrypt
|
||||
# provider (backed by the vendored libgcrypt) so ksecretd has a working
|
||||
# SymmetricCipher/Hash/PBKDF2 backend on Redox. External-toolkit providers
|
||||
# (ossl/nss/botan/gnupg/pkcs11/cyrus-sasl) are disabled — those libraries are
|
||||
# not part of the Redox userland.
|
||||
#
|
||||
# Vendored full-fork model: committed source/ tree (offline, reproducible).
|
||||
#
|
||||
# upstream: https://download.kde.org/stable/qca/ (2.3.10)
|
||||
[package]
|
||||
name = "qca"
|
||||
version = "2.3.10"
|
||||
|
||||
[source]
|
||||
path = "source"
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
dependencies = [
|
||||
"qtbase",
|
||||
"qt5compat",
|
||||
"libgcrypt",
|
||||
"libgpg-error",
|
||||
]
|
||||
script = """
|
||||
DYNAMIC_INIT
|
||||
|
||||
HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build"
|
||||
|
||||
for qtdir in plugins mkspecs metatypes modules qml; do
|
||||
if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then
|
||||
ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}"
|
||||
fi
|
||||
done
|
||||
|
||||
rm -f CMakeCache.txt
|
||||
rm -rf CMakeFiles
|
||||
|
||||
cmake "${COOKBOOK_SOURCE}" \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
||||
-DQT_HOST_PATH="${HOST_BUILD}" \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
||||
-DBUILD_WITH_QT6=ON \
|
||||
-DBUILD_TESTS=OFF \
|
||||
-DBUILD_TOOLS=OFF \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DWITH_gcrypt_PLUGIN=yes \
|
||||
-DWITH_ossl_PLUGIN=no \
|
||||
-DWITH_nss_PLUGIN=no \
|
||||
-DWITH_botan_PLUGIN=no \
|
||||
-DWITH_gnupg_PLUGIN=no \
|
||||
-DWITH_pkcs11_PLUGIN=no \
|
||||
-DWITH_cyrus-sasl_PLUGIN=no \
|
||||
-DWITH_logger_PLUGIN=yes \
|
||||
-DWITH_softstore_PLUGIN=yes \
|
||||
-Wno-dev
|
||||
cmake --build . -j${COOKBOOK_MAKE_JOBS}
|
||||
# QCA anchors all its install dirs to the absolute Qt prefix (/usr), so
|
||||
# --install --prefix is ignored; DESTDIR-stage instead (/usr/... -> STAGE/usr/...).
|
||||
DESTDIR="${COOKBOOK_STAGE}" cmake --install .
|
||||
|
||||
for lib in "${COOKBOOK_STAGE}/usr/lib/"libqca-qt6.so.* "${COOKBOOK_STAGE}/usr/lib/qca-qt6/crypto/"*.so; do
|
||||
[ -f "${lib}" ] || continue
|
||||
patchelf --remove-rpath "${lib}" 2>/dev/null || true
|
||||
done
|
||||
"""
|
||||
@@ -0,0 +1,4 @@
|
||||
# _clang_format added
|
||||
f62a8ee8f7e81a1b573c335ded3326d8ee985ab4
|
||||
# Moved to clang format 13
|
||||
27d76c096d384618453680ab4f7c3c24316e3167
|
||||
@@ -0,0 +1,7 @@
|
||||
/.clang-format
|
||||
/compile_commands.json
|
||||
.clangd
|
||||
.cache
|
||||
.idea
|
||||
.vscode
|
||||
/cmake-build*
|
||||
@@ -0,0 +1,92 @@
|
||||
include:
|
||||
- project: sysadmin/ci-utilities
|
||||
file:
|
||||
- /gitlab-templates/linux.yml
|
||||
- /gitlab-templates/linux-qt6.yml
|
||||
- /gitlab-templates/freebsd.yml
|
||||
- /gitlab-templates/freebsd-qt6.yml
|
||||
- /gitlab-templates/android-qt6.yml
|
||||
|
||||
build_ubuntu_20_10:
|
||||
stage: build
|
||||
image: ubuntu:groovy
|
||||
tags:
|
||||
- Linux
|
||||
only:
|
||||
- merge_requests
|
||||
before_script:
|
||||
- sed -i -e 's/# deb-src/deb-src/g' /etc/apt/sources.list
|
||||
- sed -i -e 's/archive/old-releases/g' /etc/apt/sources.list
|
||||
- sed -i -e 's/security.ubuntu/old-releases.ubuntu/g' /etc/apt/sources.list
|
||||
- echo "91.189.91.124 old-releases.ubuntu.com" >> /etc/hosts
|
||||
- apt update
|
||||
- apt install --yes eatmydata
|
||||
- eatmydata apt build-dep --yes --no-install-recommends qca2
|
||||
- eatmydata apt install --yes --no-install-recommends ninja-build libbotan-2-dev libnss3-dev libgcrypt20-dev libpkcs11-helper1-dev gnupg
|
||||
script:
|
||||
- mkdir -p build && cd build
|
||||
- cmake -G Ninja .. -DBUILD_PLUGINS=all
|
||||
- ninja
|
||||
- QT_PLUGIN_PATH=`pwd`/lib/qca-qt5/ ctest -V
|
||||
|
||||
build_fedora_34:
|
||||
stage: build
|
||||
image: fedora:34
|
||||
tags:
|
||||
- Linux
|
||||
only:
|
||||
- merge_requests
|
||||
before_script:
|
||||
- dnf -y --setopt=install_weak_deps=False install git gcc-c++ make cmake ninja-build qt6-qtbase-devel qt6-qt5compat-devel botan2-devel cyrus-sasl-devel nss-devel libgcrypt-devel pkcs11-helper-devel gnupg
|
||||
script:
|
||||
- mkdir -p build && cd build
|
||||
- cmake -G Ninja -DBUILD_WITH_QT6=ON .. -DBUILD_PLUGINS=all
|
||||
- ninja
|
||||
- QT_PLUGIN_PATH=`pwd`/lib/qca-qt6/ ctest -V
|
||||
|
||||
build_openssl3_botan3:
|
||||
stage: build
|
||||
image: debian:unstable
|
||||
tags:
|
||||
- Linux
|
||||
only:
|
||||
- merge_requests
|
||||
before_script:
|
||||
- echo 'deb-src http://deb.debian.org/debian unstable main' >> /etc/apt/sources.list
|
||||
- apt-get update
|
||||
- apt-get install --yes eatmydata
|
||||
- eatmydata apt-get build-dep --yes --no-install-recommends qca2
|
||||
- eatmydata apt-get install --yes --no-install-recommends ninja-build libnss3-dev libgcrypt20-dev libpkcs11-helper1-dev gnupg wget libz-dev
|
||||
script:
|
||||
- wget https://github.com/randombit/botan/archive/refs/tags/3.0.0.tar.gz
|
||||
- tar xvfh 3.0.0.tar.gz
|
||||
- cd botan-3.0.0
|
||||
- ./configure.py --prefix="/usr" --with-zlib
|
||||
- time make -j `nproc` install
|
||||
- cd ..
|
||||
- mkdir -p build && cd build
|
||||
- cmake -G Ninja .. -DBUILD_PLUGINS=all
|
||||
- ninja
|
||||
- QT_PLUGIN_PATH=`pwd`/lib/qca-qt5/ bin/qcatool* --debug plugins > features-with-legacy
|
||||
- QT_PLUGIN_PATH=`pwd`/lib/qca-qt5/ ctest -V
|
||||
- echo "Now test without the legacy provider"
|
||||
- ls /usr/lib/x86_64-linux-gnu/ossl-modules/legacy.so
|
||||
- rm /usr/lib/x86_64-linux-gnu/ossl-modules/legacy.so
|
||||
- QT_PLUGIN_PATH=`pwd`/lib/qca-qt5/ bin/qcatool* --debug plugins > features-without-legacy
|
||||
- QT_PLUGIN_PATH=`pwd`/lib/qca-qt5/ ctest -V
|
||||
- echo "Make sure there is a difference without the legacy provider"
|
||||
- "! diff -u features-with-legacy features-without-legacy"
|
||||
|
||||
clang_format:
|
||||
stage: build
|
||||
image: debian:testing
|
||||
tags:
|
||||
- Linux
|
||||
only:
|
||||
- merge_requests
|
||||
before_script:
|
||||
- apt-get update
|
||||
- apt-get install --yes --no-install-recommends git clang-format-19
|
||||
script:
|
||||
- find . \( -name "*.cpp" -or -name "*.h" -or -name "*.c" -or -name "*.cc" \) -exec clang-format-19 -i {} \;
|
||||
- git diff --exit-code
|
||||
@@ -0,0 +1,508 @@
|
||||
# Checking for user explicitly defined CMAKE_INSTALL_PREFIX
|
||||
# It must be done before project(...)
|
||||
if(NOT CMAKE_INSTALL_PREFIX)
|
||||
set(QCA_INSTALL_IN_QT_PREFIX ON)
|
||||
# If CMAKE_INSTALL_PREFIX is set in cmake arguments
|
||||
unset(CMAKE_INSTALL_PREFIX CACHE)
|
||||
endif()
|
||||
|
||||
# The cmake min. version should be set before calling project(...) too
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(qca)
|
||||
|
||||
set(QCA_LIB_MAJOR_VERSION "2")
|
||||
set(QCA_LIB_MINOR_VERSION "3")
|
||||
set(QCA_LIB_PATCH_VERSION "10")
|
||||
|
||||
set(CMAKE_MACOSX_RPATH OFF)
|
||||
|
||||
option(BUILD_WITH_QT6 "Build with Qt 6" OFF)
|
||||
option(BUILD_TESTS "Create test" ON)
|
||||
option(BUILD_TOOLS "Compile mozcerts and qcatool" ON)
|
||||
set(BUILD_PLUGINS "auto" CACHE STRING "Plugins for building (also possible values: none, all and auto)")
|
||||
# BUILD_SHARED_LIBS is cmake variable. Need to change default value.
|
||||
option(BUILD_SHARED_LIBS "Build shared library" ON)
|
||||
option(DEVELOPER_MODE "Special developer mode" OFF)
|
||||
|
||||
if(QT6)
|
||||
set(BUILD_WITH_QT6 ON)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
option(OSX_FRAMEWORK "Build a Mac OS X Framework" ON)
|
||||
else()
|
||||
set(OSX_FRAMEWORK OFF)
|
||||
endif()
|
||||
|
||||
find_package(Doxygen)
|
||||
|
||||
string(TOLOWER "${BUILD_PLUGINS}" BUILD_PLUGINS)
|
||||
if(NOT BUILD_PLUGINS)
|
||||
set(BUILD_PLUGINS "none")
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" )
|
||||
|
||||
# Do not automatically link Qt executables to qtmain target on Windows.
|
||||
# QCA exucatables use console mode only. Not need to link against qtmain.lib.
|
||||
if(BUILD_WITH_QT6)
|
||||
set(QT_MAJOR_VERSION 6)
|
||||
message(STATUS "Building Qt 6 version")
|
||||
find_package(Qt6 COMPONENTS Core Core5Compat REQUIRED)
|
||||
if(BUILD_TESTS)
|
||||
find_package(Qt6 COMPONENTS Test REQUIRED)
|
||||
endif()
|
||||
else()
|
||||
set(QT_MAJOR_VERSION 5)
|
||||
message(STATUS "Building Qt 5 version")
|
||||
set(Qt5_NO_LINK_QTMAIN ON)
|
||||
find_package(Qt5 5.14 REQUIRED Core)
|
||||
endif()
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
include(QcaMacro)
|
||||
include(GNUInstallDirs)
|
||||
if(BUILD_WITH_QT6)
|
||||
set(QCA_QT_PC_VERSION "Qt6Core")
|
||||
set(QCA_SUFFIX "qt6")
|
||||
else()
|
||||
set(QCA_QT_PC_VERSION "Qt5Core")
|
||||
set(QCA_SUFFIX "qt5")
|
||||
endif()
|
||||
|
||||
# QCA can be shared but plugins will be static
|
||||
# if Qt is static.
|
||||
if(NOT BUILD_SHARED_LIBS OR QT_IS_STATIC)
|
||||
set(STATIC_PLUGINS ON)
|
||||
add_definitions(-DQT_STATICPLUGIN)
|
||||
set(PLUGIN_TYPE "STATIC")
|
||||
else()
|
||||
set(PLUGIN_TYPE "MODULE")
|
||||
endif()
|
||||
|
||||
set(QCA_SUFFIX "${QCA_SUFFIX}" CACHE STRING "QCA common suffix")
|
||||
if(QCA_SUFFIX)
|
||||
set(QCA_LIB_NAME qca-${QCA_SUFFIX})
|
||||
set(QCA_TOOL_NAME qcatool-${QCA_SUFFIX})
|
||||
set(MOZCERTS_NAME mozcerts-${QCA_SUFFIX})
|
||||
set(QCA_PC_NAME qca2-${QCA_SUFFIX}.pc)
|
||||
set(QCA_CONFIG_NAME_BASE "Qca-${QCA_SUFFIX}")
|
||||
else()
|
||||
set(QCA_LIB_NAME qca)
|
||||
set(QCA_TOOL_NAME qcatool)
|
||||
set(MOZCERTS_NAME mozcerts)
|
||||
set(QCA_PC_NAME qca2.pc)
|
||||
set(QCA_CONFIG_NAME_BASE "Qca")
|
||||
endif()
|
||||
|
||||
set(QCA_LIB_VERSION_STRING "${QCA_LIB_MAJOR_VERSION}.${QCA_LIB_MINOR_VERSION}.${QCA_LIB_PATCH_VERSION}")
|
||||
|
||||
configure_file("include/QtCrypto/qca_version.h.in" "${CMAKE_BINARY_DIR}/qca_version.h")
|
||||
|
||||
if (WIN32)
|
||||
set(CMAKE_DEBUG_POSTFIX "d")
|
||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
||||
elseif (APPLE)
|
||||
set(CMAKE_DEBUG_POSTFIX "_debug")
|
||||
endif()
|
||||
|
||||
# C++ standard
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES Linux)
|
||||
add_definitions (-D_DEFAULT_SOURCE)
|
||||
# on arm -Wcast-align throws many internal qt warning
|
||||
if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-align")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-align")
|
||||
endif()
|
||||
|
||||
set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long -Wundef -Werror-implicit-function-declaration -Wchar-subscripts -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common")
|
||||
set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wno-long-long -Wundef -Wchar-subscripts -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security -fno-check-new -fno-common")
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override -Wlogical-op" )
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_definitions(-DQT_NO_CAST_TO_ASCII
|
||||
-DQT_NO_CAST_FROM_ASCII
|
||||
-DQT_NO_URL_CAST_FROM_STRING
|
||||
-DQT_NO_CAST_FROM_BYTEARRAY
|
||||
-DQT_NO_SIGNALS_SLOTS_KEYWORDS
|
||||
-DQT_USE_QSTRINGBUILDER
|
||||
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT)
|
||||
|
||||
if (NOT WIN32)
|
||||
# Strict iterators can't be used on Windows, they lead to a link error
|
||||
# when application code iterates over a QVector<QPoint> for instance, unless
|
||||
# Qt itself was also built with strict iterators.
|
||||
# See example at https://bugreports.qt.io/browse/AUTOSUITE-946
|
||||
add_definitions(-DQT_STRICT_ITERATORS)
|
||||
endif()
|
||||
|
||||
include_directories("include/QtCrypto/")
|
||||
# for generated files
|
||||
include_directories(${CMAKE_BINARY_DIR})
|
||||
|
||||
if(NOT DEVELOPER_MODE)
|
||||
# uninstall target
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||
endif()
|
||||
|
||||
# Always include srcdir and builddir in include path
|
||||
# This saves typing ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY} in about every subdir
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
# put the include dirs which are in the source or build tree
|
||||
# before all other include dirs, so the headers in the sources
|
||||
# are prefered over the already installed ones
|
||||
set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON)
|
||||
|
||||
set(qca_INCLUDEDIR "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" )
|
||||
# Use the same path for shared and static library
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" )
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" )
|
||||
|
||||
if( QCA_INSTALL_IN_QT_PREFIX )
|
||||
setup_qt_dirs()
|
||||
set(QCA_PREFIX_INSTALL_DIR "${QT_PREFIX_DIR}" CACHE PATH "Directory where qca will install")
|
||||
set(QCA_PLUGINS_INSTALL_DIR "${QT_PLUGINS_DIR}" CACHE PATH "Directory where qca plugins will install")
|
||||
set(QCA_BINARY_INSTALL_DIR "${QT_BINARY_DIR}" CACHE PATH "Directory where qca plugins will install")
|
||||
set(QCA_LIBRARY_INSTALL_DIR "${QT_LIBRARY_DIR}" CACHE PATH "Directory where qca library will install")
|
||||
set(QCA_FEATURE_INSTALL_DIR "${QT_MKSPECS_DIR}/features" CACHE PATH "Directory where qca feature file will install")
|
||||
if(NOT QCA_SUFFIX)
|
||||
set(QCA_INCLUDE_INSTALL_DIR "${QT_HEADERS_DIR}" CACHE PATH "Directory where qca public headers will install")
|
||||
set(QCA_PRIVATE_INCLUDE_INSTALL_DIR "${QT_HEADERS_DIR}" CACHE PATH "Directory where qca headers will install")
|
||||
else()
|
||||
set(QCA_INCLUDE_INSTALL_DIR "${QT_HEADERS_DIR}/${QCA_CONFIG_NAME_BASE}" CACHE PATH "Directory where qca public headers will install")
|
||||
set(QCA_PRIVATE_INCLUDE_INSTALL_DIR "${QT_HEADERS_DIR}/${QCA_CONFIG_NAME_BASE}" CACHE PATH "Directory where qca headers will install")
|
||||
endif()
|
||||
set(QCA_DOC_INSTALL_DIR "${QT_DOC_DIR}/html/qca/" CACHE PATH "Directory where qca documentation will install")
|
||||
set(QCA_MAN_INSTALL_DIR "${QT_DATA_DIR}/man" CACHE PATH "Directory where qca man pages will install")
|
||||
else()
|
||||
# Cmake says nothing about LIB_SUFFIX
|
||||
# de facto it is a standard way to specify lib suffix on many distros
|
||||
set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" )
|
||||
set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "Directory where lib will install")
|
||||
|
||||
set(QCA_PREFIX_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Directory where qca will install")
|
||||
set(QCA_PLUGINS_INSTALL_DIR "${LIB_INSTALL_DIR}/${QCA_LIB_NAME}" CACHE PATH "Directory where qca plugins will install")
|
||||
set(QCA_BINARY_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Directory where qca plugins will install")
|
||||
set(QCA_LIBRARY_INSTALL_DIR "${LIB_INSTALL_DIR}" CACHE PATH "Directory where qca library will install")
|
||||
set(QCA_FEATURE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/mkspecs/features" CACHE PATH "Directory where qca feature file will install")
|
||||
if(NOT QCA_SUFFIX)
|
||||
set(QCA_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "Directory where qca public headers will install")
|
||||
set(QCA_PRIVATE_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "Directory where qca headers will install")
|
||||
else()
|
||||
set(QCA_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include/${QCA_CONFIG_NAME_BASE}" CACHE PATH "Directory where qca public headers will install")
|
||||
set(QCA_PRIVATE_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include/${QCA_CONFIG_NAME_BASE}" CACHE PATH "Directory where qca headers will install")
|
||||
endif()
|
||||
set(QCA_DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/${QCA_LIB_NAME}/html" CACHE PATH "Directory where qca documentation will install")
|
||||
set(QCA_MAN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/man" CACHE PATH "Directory where qca man pages will install")
|
||||
endif()
|
||||
|
||||
set(PKGCONFIG_INSTALL_PREFIX "${QCA_LIBRARY_INSTALL_DIR}/pkgconfig" CACHE PATH "Base directory for pkgconfig files")
|
||||
|
||||
normalize_path(QCA_PREFIX_INSTALL_DIR)
|
||||
|
||||
if(OSX_FRAMEWORK)
|
||||
set(QCA_FULL_INCLUDE_INSTALL_DIR "${QCA_LIBRARY_INSTALL_DIR}/${QCA_LIB_NAME}.framework/Headers")
|
||||
else()
|
||||
set(QCA_FULL_INCLUDE_INSTALL_DIR "${QCA_INCLUDE_INSTALL_DIR}/QtCrypto")
|
||||
endif()
|
||||
|
||||
# check for oportunity to use relative paths
|
||||
option(USE_RELATIVE_PATHS "Try to make relocatable package")
|
||||
|
||||
foreach(PATH QCA_PLUGINS_INSTALL_DIR
|
||||
QCA_BINARY_INSTALL_DIR
|
||||
QCA_LIBRARY_INSTALL_DIR
|
||||
QCA_FEATURE_INSTALL_DIR
|
||||
QCA_INCLUDE_INSTALL_DIR
|
||||
QCA_PRIVATE_INCLUDE_INSTALL_DIR
|
||||
QCA_DOC_INSTALL_DIR
|
||||
QCA_MAN_INSTALL_DIR
|
||||
PKGCONFIG_INSTALL_PREFIX)
|
||||
|
||||
# Normalize path before comparsion
|
||||
normalize_path(${PATH})
|
||||
|
||||
# if all paths are subdirs of CMAKE_INSTALL_PREFIX it is possible to use relative paths
|
||||
string(FIND "${${PATH}}" "${QCA_PREFIX_INSTALL_DIR}/" POS)
|
||||
if(NOT "${POS}" STREQUAL "0")
|
||||
set(USE_RELATIVE_PATHS OFF)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT WIN32)
|
||||
if(OSX_FRAMEWORK)
|
||||
set(PKGCONFIG_CFLAGS "-F\${libdir} -I\${includedir}")
|
||||
set(PKGCONFIG_LIBS "-F\${libdir} -framework ${QCA_LIB_NAME}")
|
||||
else()
|
||||
set(PKGCONFIG_CFLAGS "-I\${includedir}")
|
||||
set(PKGCONFIG_LIBS "-L\${libdir} -l${QCA_LIB_NAME}")
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_WITH_QT6)
|
||||
# qca2.pc uses absolute paths. So it must be there. Don't rellocate this.
|
||||
configure_file("qca2.pc.cmake" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/pkgconfig/${QCA_PC_NAME}" @ONLY)
|
||||
if(NOT DEVELOPER_MODE)
|
||||
install(FILES "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/pkgconfig/${QCA_PC_NAME}" DESTINATION ${PKGCONFIG_INSTALL_PREFIX})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# strip CMAKE_INSTALL_PREFIX in all paths
|
||||
if(USE_RELATIVE_PATHS)
|
||||
message(STATUS "Installed package is relocatable")
|
||||
file(RELATIVE_PATH CRYPTO_PRF_RELATIVE_PATH "${QCA_FEATURE_INSTALL_DIR}" ${CMAKE_INSTALL_PREFIX})
|
||||
set(CRYPTO_PRF_RELATIVE_PATH "$$PWD/${CRYPTO_PRF_RELATIVE_PATH}")
|
||||
foreach(PATH QCA_PLUGINS_INSTALL_DIR
|
||||
QCA_BINARY_INSTALL_DIR
|
||||
QCA_LIBRARY_INSTALL_DIR
|
||||
QCA_FEATURE_INSTALL_DIR
|
||||
QCA_INCLUDE_INSTALL_DIR
|
||||
QCA_PRIVATE_INCLUDE_INSTALL_DIR
|
||||
QCA_DOC_INSTALL_DIR
|
||||
QCA_MAN_INSTALL_DIR
|
||||
PKGCONFIG_INSTALL_PREFIX)
|
||||
|
||||
file(RELATIVE_PATH ${PATH} ${CMAKE_INSTALL_PREFIX} "${${PATH}}")
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS "Installed package is NOT relocatable")
|
||||
set(CRYPTO_PRF_RELATIVE_PATH "")
|
||||
endif()
|
||||
|
||||
if(DEVELOPER_MODE)
|
||||
add_definitions(-DDEVELOPER_MODE)
|
||||
|
||||
# To prefer plugins from build tree when run qca from build tree
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/bin/qt.conf
|
||||
"[Paths]
|
||||
Plugins=${CMAKE_BINARY_DIR}/lib/${QCA_LIB_NAME}
|
||||
")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
endif()
|
||||
|
||||
message(STATUS "Checking for certstore..")
|
||||
# fixme add OR mac
|
||||
if( WIN32 )
|
||||
# USE BUILTIN
|
||||
else()
|
||||
if ( DEFINED ENV{QC_CERTSTORE_PATH} )
|
||||
if(EXISTS $ENV{QC_CERTSTORE_PATH})
|
||||
set( qca_CERTSTORE $ENV{QC_CERTSTORE_PATH})
|
||||
endif()
|
||||
else()
|
||||
set( toTry
|
||||
"/etc/ssl/certs/ca-certificates.crt"
|
||||
"/usr/share/ssl/cert.pem"
|
||||
"/usr/share/ssl/certs/ca-bundle.crt"
|
||||
"/etc/pki/tls/cert.pem"
|
||||
"/etc/ssl/ca-bundle.pem"
|
||||
"/usr/share/curl/curl-ca-bundle.crt"
|
||||
)
|
||||
foreach (_current_try ${toTry})
|
||||
if(EXISTS ${_current_try})
|
||||
set( qca_CERTSTORE ${_current_try})
|
||||
endif()
|
||||
endforeach (_current_try)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (qca_CERTSTORE)
|
||||
message(STATUS "Found system certstore")
|
||||
else()
|
||||
message(STATUS "Using built in certstore.")
|
||||
set( qca_CERTSTORE "${CMAKE_CURRENT_SOURCE_DIR}/certs/rootcerts.pem")
|
||||
# note that INSTALL_FILES targets are relative to the current installation prefix...
|
||||
if(NOT DEVELOPER_MODE)
|
||||
install(FILES "${qca_CERTSTORE}" DESTINATION "${QCA_PREFIX_INSTALL_DIR}/certs")
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "certstore path: " ${qca_CERTSTORE})
|
||||
add_definitions( -DQCA_SYSTEMSTORE_PATH="${qca_CERTSTORE}" )
|
||||
|
||||
|
||||
set( private_HEADERS ${qca_INCLUDEDIR}/QtCrypto/qca_plugin.h ${qca_INCLUDEDIR}/QtCrypto/qca_systemstore.h )
|
||||
|
||||
set( public_HEADERS
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qcaprovider.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/QtCrypto
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca_support.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca_tools.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca_core.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca_textfilter.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca_basic.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca_publickey.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca_cert.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca_keystore.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca_securelayer.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca_securemessage.h
|
||||
${CMAKE_BINARY_DIR}/qca_version.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qpipe.h
|
||||
${qca_INCLUDEDIR}/QtCrypto/qca_safetimer.h)
|
||||
|
||||
set( qca_HEADERS ${private_HEADERS} ${public_HEADERS} )
|
||||
|
||||
include_directories("${qca_INCLUDEDIR}/QtCrypto")
|
||||
|
||||
if(NOT BUILD_WITH_QT6)
|
||||
configure_file("crypto.prf.cmake" "${CMAKE_BINARY_DIR}/mkspecs/features/crypto.prf" @ONLY)
|
||||
if(NOT DEVELOPER_MODE)
|
||||
install(FILES "${CMAKE_BINARY_DIR}/mkspecs/features/crypto.prf" DESTINATION "${QCA_FEATURE_INSTALL_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
configure_file(man/qcatool.1 "${CMAKE_BINARY_DIR}/share/man/man1/${QCA_TOOL_NAME}.1" COPYONLY)
|
||||
if(NOT DEVELOPER_MODE)
|
||||
install(FILES "${CMAKE_BINARY_DIR}/share/man/man1/${QCA_TOOL_NAME}.1" DESTINATION "${QCA_MAN_INSTALL_DIR}/man1")
|
||||
endif()
|
||||
|
||||
set(QCA_CRYPTO_INSTALL_DIR "${QCA_PLUGINS_INSTALL_DIR}/crypto")
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(plugins)
|
||||
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(${QCA_LIB_NAME} BASE_NAME qca)
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/qca_export.h DESTINATION ${QCA_FULL_INCLUDE_INSTALL_DIR}
|
||||
)
|
||||
|
||||
if(STATIC_PLUGINS)
|
||||
# Generate header with static plugins list
|
||||
file(WRITE "${CMAKE_BINARY_DIR}/import_plugins.h" "#include <QtPlugin>\n")
|
||||
foreach(PLUGIN IN LISTS PLUGINS)
|
||||
if(WITH_${PLUGIN}_PLUGIN_INTERNAL)
|
||||
string(REPLACE "-" "_" IMPORT_NAME "qca-${PLUGIN}")
|
||||
file(APPEND "${CMAKE_BINARY_DIR}/import_plugins.h" "Q_IMPORT_PLUGIN(${IMPORT_NAME})\n")
|
||||
endif()
|
||||
endforeach(PLUGIN IN LISTS PLUGINS)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(unittest)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
if(BUILD_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
endif()
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
configure_file(${CMAKE_SOURCE_DIR}/Doxyfile.in ${CMAKE_BINARY_DIR}/Doxyfile @ONLY)
|
||||
add_custom_target(doc
|
||||
${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/docs/pics
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/docs/pics/qca-arch.eps ${CMAKE_BINARY_DIR}/docs/pics/qca-arch.eps
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/docs/pics/qca-arch.png ${CMAKE_BINARY_DIR}/docs/pics/qca-arch.png
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen" VERBATIM)
|
||||
endif()
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/QcaConfig.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/${QCA_CONFIG_NAME_BASE}/${QCA_CONFIG_NAME_BASE}Config.cmake"
|
||||
INSTALL_DESTINATION ${QCA_LIBRARY_INSTALL_DIR}/cmake/${QCA_CONFIG_NAME_BASE}
|
||||
)
|
||||
write_basic_config_version_file("${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/${QCA_CONFIG_NAME_BASE}/${QCA_CONFIG_NAME_BASE}ConfigVersion.cmake" VERSION ${QCA_LIB_VERSION_STRING} COMPATIBILITY AnyNewerVersion)
|
||||
|
||||
if(NOT DEVELOPER_MODE)
|
||||
|
||||
# /usr/local is traditional path for installing apps on POSIX-systems.
|
||||
# I consciously break this. Qt by default looks plugins and features only in
|
||||
# own directory. So by default install libs in Qt prefix it is a best choice.
|
||||
# This can be unwanted behaviour for users who don't read INSTALL file or/and
|
||||
# not read cmake reports. I just try to warn their.
|
||||
|
||||
# In really anybody who do cmake . && make && sudo make install do it for own risk.
|
||||
|
||||
if(QCA_INSTALL_IN_QT_PREFIX)
|
||||
string(ASCII 27 ESCAPE)
|
||||
message("")
|
||||
message("${ESCAPE}[31m")
|
||||
message("!!!!!!!!!!!!!!!!!!!!!ATTENTION!!!!!!!!!!!!!!!!!!!!!!")
|
||||
message("!! QCA will be installed in Qt prefix !!")
|
||||
message("!! If you want to install in /usr/local !!")
|
||||
message("!! you MUST explicity define CMAKE_INSTALL_PREFIX !!")
|
||||
message("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
||||
message("${ESCAPE}[0m")
|
||||
endif()
|
||||
|
||||
message("")
|
||||
if(USE_RELATIVE_PATHS)
|
||||
message("QCA prefix is " "${QCA_PREFIX_INSTALL_DIR}")
|
||||
message("Plugins will be installed to " "${QCA_PREFIX_INSTALL_DIR}/${QCA_PLUGINS_INSTALL_DIR}")
|
||||
message("Binary will be installed to " "${QCA_PREFIX_INSTALL_DIR}/${QCA_BINARY_INSTALL_DIR}")
|
||||
if(OSX_FRAMEWORK)
|
||||
message("Framework will be installed to " "${QCA_PREFIX_INSTALL_DIR}/${QCA_LIBRARY_INSTALL_DIR}")
|
||||
else()
|
||||
message("Library will be installed to " "${QCA_PREFIX_INSTALL_DIR}/${QCA_LIBRARY_INSTALL_DIR}")
|
||||
message("Public headers will be installed to " "${QCA_PREFIX_INSTALL_DIR}/${QCA_INCLUDE_INSTALL_DIR}")
|
||||
message("Private headers will be installed to " "${QCA_PREFIX_INSTALL_DIR}/${QCA_PRIVATE_INCLUDE_INSTALL_DIR}")
|
||||
endif()
|
||||
message("Feature file will be installed to " "${QCA_PREFIX_INSTALL_DIR}/${QCA_FEATURE_INSTALL_DIR}")
|
||||
message("Documentation will be installed to " "${QCA_PREFIX_INSTALL_DIR}/${QCA_DOC_INSTALL_DIR}")
|
||||
message("Man page will be installed to " "${QCA_PREFIX_INSTALL_DIR}/${QCA_MAN_INSTALL_DIR}")
|
||||
message("Pkg-config file will be installed to " "${QCA_PREFIX_INSTALL_DIR}/${PKGCONFIG_INSTALL_PREFIX}")
|
||||
else()
|
||||
message("QCA prefix is " "${QCA_PREFIX_INSTALL_DIR}")
|
||||
message("Plugins will be installed to " "${QCA_PLUGINS_INSTALL_DIR}")
|
||||
message("Binary will be installed to " "${QCA_BINARY_INSTALL_DIR}")
|
||||
if(OSX_FRAMEWORK)
|
||||
message("Framework will be installed to " "${QCA_LIBRARY_INSTALL_DIR}")
|
||||
else()
|
||||
message("Library will be installed to " "${QCA_LIBRARY_INSTALL_DIR}")
|
||||
message("Public headers will be installed to " "${QCA_INCLUDE_INSTALL_DIR}")
|
||||
message("Private headers will be installed to " "${QCA_PRIVATE_INCLUDE_INSTALL_DIR}")
|
||||
endif()
|
||||
message("Feature file will be installed to " "${QCA_FEATURE_INSTALL_DIR}")
|
||||
message("Documentation will be installed to " "${QCA_DOC_INSTALL_DIR}")
|
||||
message("Man page will be installed to " "${QCA_MAN_INSTALL_DIR}")
|
||||
message("Pkg-config file will be installed to " "${PKGCONFIG_INSTALL_PREFIX}")
|
||||
endif()
|
||||
message("")
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(NOT QCA_SUFFIX)
|
||||
|
||||
message("${ESCAPE}[31mYou don't have QCA_SUFFIX set. Please note that the recommended way of")
|
||||
if(BUILD_WITH_QT6)
|
||||
message("building Qt6 version of qca for Linux distributions is to set")
|
||||
message("QCA_SUFFIX to qt6 (-DQCA_SUFFIX=qt6).")
|
||||
else()
|
||||
message("building Qt5 version of qca for Linux distributions is to set")
|
||||
message("QCA_SUFFIX to qt5 (-DQCA_SUFFIX=qt5).")
|
||||
endif()
|
||||
message("${ESCAPE}[0m")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(EXPORT ${QCA_CONFIG_NAME_BASE}Targets DESTINATION ${QCA_LIBRARY_INSTALL_DIR}/cmake/${QCA_CONFIG_NAME_BASE} FILE ${QCA_CONFIG_NAME_BASE}Targets.cmake)
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/${QCA_CONFIG_NAME_BASE}/${QCA_CONFIG_NAME_BASE}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/${QCA_CONFIG_NAME_BASE}/${QCA_CONFIG_NAME_BASE}ConfigVersion.cmake"
|
||||
DESTINATION ${QCA_LIBRARY_INSTALL_DIR}/cmake/${QCA_CONFIG_NAME_BASE}
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,504 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
Installing QCA
|
||||
--------------
|
||||
|
||||
For Unix/Linux/Mac/Windows:
|
||||
|
||||
cmake .
|
||||
make
|
||||
make install
|
||||
/sbin/ldconfig, if necessary
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
Use cmake cache entries
|
||||
|
||||
CMAKE_INSTALL_PREFIX - must be defined otherwise QCA will be installed
|
||||
in Qt prefix (by default: "")
|
||||
BUILD_TESTS - build unittests (by default: ON)
|
||||
BUILD_TOOLS - build mozcerts and qcatool (by default: ON)
|
||||
QCA_SUFFIX - suffix will be used for library, qcatool binary,
|
||||
qcatool manpage and qca pkg-config file (by default: "")
|
||||
LIB_SUFFIX - library directory suffix (by default: "")
|
||||
LIB_INSTALL_DIR - path to library directory
|
||||
(by default: ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX})
|
||||
BUILD_PLUGINS - list plugins to build (botan;ossl;gnupg for example).
|
||||
Also possible values is none, auto or all
|
||||
(by default: "auto")
|
||||
WITH_${PLUGIN}_PLUGIN - to build particular plugin. Can be no, yes or
|
||||
auto (WITH_ossl_PLUGIN=auto for example)
|
||||
DEVELOPER_MODE - mode to enable features for developers. If ON
|
||||
will be used hardcoded path to 'crypto'
|
||||
(by default: OFF)
|
||||
OSX_FRAMEWORK - build an OS X framework (by default: ON)
|
||||
USE_RELATIVE_PATHS - make relocatable package if possible (by default: OFF)
|
||||
|
||||
Finally install paths can be override with:
|
||||
|
||||
QCA_PREFIX_INSTALL_DIR - qca prefix, just a origin to make other paths
|
||||
QCA_PLUGINS_INSTALL_DIR - qt plugins path
|
||||
QCA_BINARY_INSTALL_DIR - qcatool will be installed to
|
||||
QCA_LIBRARY_INSTALL_DIR - qca library will be installed to
|
||||
QCA_FEATURE_INSTALL_DIR - path to qt mkspecs dir
|
||||
QCA_INCLUDE_INSTALL_DIR - path for QtCrypto dir with includes
|
||||
QCA_PRIVATE_INCLUDE_INSTALL_DIR - for future implementation
|
||||
QCA_DOC_INSTALL_DIR - for html documentation
|
||||
QCA_MAN_INSTALL_DIR - for qcatool manpage
|
||||
PKGCONFIG_INSTALL_PREFIX - path to install pkg config file
|
||||
|
||||
Please report problems to:
|
||||
http://bugs.kde.org
|
||||
|
||||
Official git repo:
|
||||
https://invent.kde.org/kde/qca
|
||||
@@ -0,0 +1,400 @@
|
||||
/**
|
||||
\mainpage Qt Cryptographic Architecture
|
||||
|
||||
Taking a hint from the similarly-named
|
||||
<a href="http://java.sun.com/j2se/1.5.0/docs/guide/security/CryptoSpec.html">Java
|
||||
Cryptography Architecture</a>, %QCA aims to provide a
|
||||
straightforward and cross-platform cryptographic API, using Qt
|
||||
datatypes and conventions. %QCA separates the API from the
|
||||
implementation, using plugins known as Providers. The advantage
|
||||
of this model is to allow applications to avoid linking to or
|
||||
explicitly depending on any particular cryptographic library.
|
||||
This allows one to easily change or upgrade Provider
|
||||
implementations without even needing to recompile the
|
||||
application!
|
||||
|
||||
%QCA should work everywhere %Qt does, including Windows/Unix/MacOSX. This
|
||||
version of %QCA is for Qt4 or Qt5, and requires no Qt3 compatibility code.
|
||||
|
||||
\section features Features
|
||||
|
||||
This library provides an easy API for the following features:
|
||||
- Secure byte arrays (QCA::SecureArray)
|
||||
- Arbitrary precision integers (QCA::BigInteger)
|
||||
- Random number generation (QCA::Random)
|
||||
- SSL/TLS (QCA::TLS)
|
||||
- X509 certificates (QCA::Certificate and QCA::CertificateCollection)
|
||||
- X509 certificate revocation lists (QCA::CRL)
|
||||
- Built-in support for operating system certificate root storage (QCA::systemStore)
|
||||
- Simple Authentication and Security Layer (SASL) (QCA::SASL)
|
||||
- Cryptographic Message Syntax (e.g., for S/MIME) (QCA::CMS)
|
||||
- PGP messages (QCA::OpenPGP)
|
||||
- Unified PGP/CMS API (QCA::SecureMessage)
|
||||
- Subsystem for managing Smart Cards and PGP keyrings (QCA::KeyStore)
|
||||
- Simple but flexible logging system (QCA::Logger)
|
||||
- RSA (QCA::RSAPrivateKey and QCA::RSAPublicKey)
|
||||
- DSA (QCA::DSAPrivateKey and QCA::DSAPublicKey)
|
||||
- Diffie-Hellman (QCA::DHPrivateKey and QCA::DHPublicKey)
|
||||
- Hashing (QCA::Hash) with
|
||||
- SHA-0
|
||||
- SHA-1
|
||||
- MD2
|
||||
- MD4
|
||||
- MD5
|
||||
- RIPEMD160
|
||||
- SHA-224
|
||||
- SHA-256
|
||||
- SHA-384
|
||||
- SHA-512
|
||||
- Whirlpool
|
||||
- Ciphers (QCA::Cipher) using
|
||||
- BlowFish
|
||||
- Triple DES
|
||||
- DES
|
||||
- AES (128, 192 and 256 bit)
|
||||
- CAST5 (also known as CAST-128)
|
||||
- Message Authentication Code (QCA::MessageAuthenticationCode), using
|
||||
- HMAC with SHA-1
|
||||
- HMAC with MD5
|
||||
- HMAC with RIPEMD160
|
||||
- HMAC with SHA-224
|
||||
- HMAC with SHA-256
|
||||
- HMAC with SHA-384
|
||||
- HMAC with SHA-512
|
||||
- Encoding and decoding of hexadecimal (QCA::Hex) and
|
||||
Base64 (QCA::Base64) strings.
|
||||
|
||||
Functionality is supplied via plugins. This is useful for avoiding
|
||||
dependence on a particular crypto library and makes upgrading easier,
|
||||
as there is no need to recompile your application when adding or
|
||||
upgrading a crypto plugin. Also, by pushing crypto functionality into
|
||||
plugins, your application is free of legal issues, such as export
|
||||
regulation.
|
||||
|
||||
And of course, you get a very simple crypto API for Qt, where you can
|
||||
do things like:
|
||||
\code
|
||||
QString hash = QCA::Hash("sha1").hashToString(blockOfData);
|
||||
\endcode
|
||||
|
||||
\section using Using QCA
|
||||
|
||||
The application simply includes <QtCrypto> and links to
|
||||
libqca, which provides the 'wrapper API' and plugin loader. Crypto
|
||||
functionality is determined during runtime, and plugins are loaded
|
||||
from the 'crypto' subfolder of the %Qt library paths. There are <a
|
||||
href="examples.html">additional examples available</a>.
|
||||
|
||||
\subsection tute1 Introduction
|
||||
|
||||
Using %QCA is much like using Qt, and if you are familiar with
|
||||
Qt, then it should feel "natural". There are a few things you
|
||||
do need to know though, to build reliable applications:
|
||||
- %QCA needs to be initialized before you use any class that
|
||||
requires plugin support, or uses secure memory. That is most
|
||||
of %QCA, so you should assume that you need to perform
|
||||
initialization. The easiest way to do this is to instantiate
|
||||
a QCA::Initializer object and ensure it is not deleted (or
|
||||
allowed to go out of scope) until you have finished using
|
||||
%QCA.
|
||||
- Most features/algorithms are provided by plugins/\ref providers.
|
||||
You should check that the required feature is actually
|
||||
available (using QCA::isSupported()) before trying to create
|
||||
it. If you try to create a class and suitable provider support
|
||||
is not available, you will get back a null object, and when
|
||||
you try to use one of the methods, your application will
|
||||
segfault. Also, for features that take algorithm names (e.g.
|
||||
QCA::Hash, which takes the name of the hashing algorithm such
|
||||
as "md5" or "sha256"), the name is looked up at run-time, so
|
||||
if you make a typographical error (e.g. "md56") it will compile
|
||||
correctly, but segfault at run-time.
|
||||
|
||||
\subsection tute2 Thoughts on security
|
||||
|
||||
%QCA tries to be flexible in what it supports. That does not mean that
|
||||
every possible combination of features makes sense though.
|
||||
|
||||
We strongly recommend against coming up with your own design made up
|
||||
of low-level cryptographic primitives (e.g. QCA::Hash, QCA::Cipher and
|
||||
similar features) and trying to use higher level capabilities. In particular,
|
||||
we recommend looking at QCA::TLS, QCA::SASL, QCA::CMS and QCA::OpenPGP
|
||||
as starting points.
|
||||
|
||||
When selecting a particular cryptographic feature, you should make sure
|
||||
that you understand what sort of threats your application is likely
|
||||
to be exposed to, and how that threat can be effectively countered. In
|
||||
addition, you should consider whether you can avoid adding cryptographic
|
||||
features directly to your application (e.g. for secure transport, you
|
||||
may be able to tunnel your application over SSH).
|
||||
|
||||
Also, you may need to look beyond %QCA for some security needs (e.g.
|
||||
for authentication, your situation may be more suited to using
|
||||
Kerberos than SASL or TLS).
|
||||
|
||||
\subsection intro-design Design
|
||||
|
||||
The architecture of %QCA is shown below:
|
||||
|
||||
\image html qca-arch.png "QCA Architecture"
|
||||
\image latex qca-arch.eps "QCA Architecture" width=\textwidth
|
||||
|
||||
Application authors normally only need to use the User API. The
|
||||
provider API is available for plugin authors, but can also
|
||||
be used by application authors to provide very specific capabilities.
|
||||
|
||||
For more information on the design of %QCA, you might like to review
|
||||
the \ref architecture description.
|
||||
|
||||
\section availability Availability
|
||||
|
||||
\subsection qca2code Releases
|
||||
|
||||
The latest release packages can be found in the
|
||||
<a href="https://download.kde.org/stable/qca/">%QCA 2.x download area</a>.
|
||||
|
||||
See the <a href="https://userbase.kde.org/QCA/">project web site</a> for
|
||||
further information about %QCA releases.
|
||||
|
||||
\subsection qca2dev Current development
|
||||
|
||||
The latest version of the code is available from the KDE Git server
|
||||
(there is no formal release of the current version at this time).
|
||||
Naturally you will need %Qt properly set up and configured in order
|
||||
to build and use %QCA.
|
||||
|
||||
The Git code can be browsed
|
||||
<a href="https://commits.kde.org/qca">
|
||||
via the web</a>
|
||||
|
||||
Use
|
||||
\verbatim
|
||||
git clone https://invent.kde.org/libraries/qca.git
|
||||
\endverbatim
|
||||
to get the latest sources.
|
||||
*/
|
||||
|
||||
/** \page architecture Architecture
|
||||
|
||||
\note You don't need to understand any of this to use %QCA - it is
|
||||
documented for those who are curious, and for anyone planning to
|
||||
extend or modify %QCA.
|
||||
|
||||
The design of %QCA is based on the Bridge design pattern. The intent of
|
||||
the Bridge pattern is to "Decouple an abstraction from its
|
||||
implementation so that the two can vary independently." [Gamma et.al,
|
||||
pg 151].
|
||||
|
||||
To understand how this decoupling works in the case of %QCA, is is
|
||||
easiest to look at an example - a cryptographic Hash. The API is
|
||||
pretty simple (although I've left out some parts that aren't required
|
||||
for this example):
|
||||
|
||||
\code
|
||||
class QCA_EXPORT Hash : public Algorithm, public BufferedComputation
|
||||
{
|
||||
public:
|
||||
Hash(const QString &type, const QString &provider);
|
||||
virtual void clear();
|
||||
virtual void update(const QCA::SecureArray &a);
|
||||
virtual QCA::SecureArray final();
|
||||
}
|
||||
\endcode
|
||||
|
||||
The implementation for the Hash class is almost as simple:
|
||||
|
||||
\code
|
||||
Hash::Hash(const QString &type, const QString &provider)
|
||||
:Algorithm(type, provider)
|
||||
{
|
||||
}
|
||||
|
||||
void Hash::clear()
|
||||
{
|
||||
static_cast<HashContext *>(context())->clear();
|
||||
}
|
||||
|
||||
void Hash::update(const QCA::SecureArray &a)
|
||||
{
|
||||
static_cast<HashContext *>(context())->update(a);
|
||||
}
|
||||
|
||||
QCA::SecureArray Hash::final()
|
||||
{
|
||||
return static_cast<HashContext *>(context())->final();
|
||||
}
|
||||
\endcode
|
||||
|
||||
The reason why it looks so simple is that the various methods in Hash
|
||||
just call out to equivalent routines in the context() object. The
|
||||
context comes from a call (getContext()) that is made as part of the
|
||||
Algorithm constructor. That getContext() call causes %QCA to work
|
||||
through the list of providers (generally plugins) that it knows about,
|
||||
looking for a provider that can produce the right kind of context (in
|
||||
this case, a HashContext).
|
||||
|
||||
The code for a HashContext doesn't need to be linked into %QCA - it can
|
||||
be varied in its implementation, including being changed at run-time. The
|
||||
application doesn't need to know how HashContext is implemented, because
|
||||
it just has to deal with the Hash class interface. In fact, HashContext
|
||||
may not be implemented, so the application should check (using
|
||||
QCA::isSupported()) before trying to use features that are implemented
|
||||
with plugins.
|
||||
|
||||
The code for one implementation (in this case, calling OpenSSL) is shown
|
||||
below.
|
||||
\code
|
||||
class opensslHashContext : public HashContext
|
||||
{
|
||||
public:
|
||||
opensslHashContext(const EVP_MD *algorithm, Provider *p, const QString &type) : HashContext(p, type)
|
||||
{
|
||||
m_algorithm = algorithm;
|
||||
EVP_DigestInit( &m_context, m_algorithm );
|
||||
};
|
||||
|
||||
~opensslHashContext()
|
||||
{
|
||||
EVP_MD_CTX_cleanup(&m_context);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
EVP_MD_CTX_cleanup(&m_context);
|
||||
EVP_DigestInit( &m_context, m_algorithm );
|
||||
}
|
||||
|
||||
void update(const QCA::SecureArray &a)
|
||||
{
|
||||
EVP_DigestUpdate( &m_context, (unsigned char*)a.data(), a.size() );
|
||||
}
|
||||
|
||||
QCA::SecureArray final()
|
||||
{
|
||||
QCA::SecureArray a( EVP_MD_size( m_algorithm ) );
|
||||
EVP_DigestFinal( &m_context, (unsigned char*)a.data(), 0 );
|
||||
return a;
|
||||
}
|
||||
|
||||
Provider::Context *clone() const
|
||||
{
|
||||
return new opensslHashContext(*this);
|
||||
}
|
||||
|
||||
protected:
|
||||
const EVP_MD *m_algorithm;
|
||||
EVP_MD_CTX m_context;
|
||||
};
|
||||
\endcode
|
||||
|
||||
This approach (using an Adapter pattern) is very common in %QCA backends,
|
||||
because the plugins are often based on existing libraries.
|
||||
|
||||
In addition to the various Context objects, each provider also has
|
||||
a parameterised Factory class that has a createContext() method, as
|
||||
shown below:
|
||||
\code
|
||||
Context *createContext(const QString &type)
|
||||
{
|
||||
//OpenSSL_add_all_digests();
|
||||
if ( type == "sha1" )
|
||||
return new opensslHashContext( EVP_sha1(), this, type);
|
||||
else if ( type == "sha0" )
|
||||
return new opensslHashContext( EVP_sha(), this, type);
|
||||
else if ( type == "md5" )
|
||||
return new opensslHashContext( EVP_md5(), this, type);
|
||||
else if ( type == "aes128-cfb" )
|
||||
return new opensslCipherContext( EVP_aes_128_cfb(), 0, this, type);
|
||||
else if ( type == "aes128-cbc" )
|
||||
return new opensslCipherContext( EVP_aes_128_cbc(), 0, this, type);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
\endcode
|
||||
|
||||
The resulting effect is that %QCA can ask the provider to provide an appropriate
|
||||
Context object without worrying about how it is implemented.
|
||||
|
||||
For features that are implemented with variable algorithms (for example, HashContext can
|
||||
support a wide range of algorithms - MD5, SHA0, and SHA1 in the example above; and
|
||||
CipherContext and MACContext can also do this), we need to be able to let applications
|
||||
determine which algorithms are supported. This is handled through the InfoContext class.
|
||||
A typical example is shown below:
|
||||
\code
|
||||
class opensslInfoContext : public InfoContext
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
opensslInfoContext(Provider *p) : InfoContext(p)
|
||||
{
|
||||
}
|
||||
|
||||
Context *clone() const
|
||||
{
|
||||
return new opensslInfoContext(*this);
|
||||
}
|
||||
|
||||
QStringList supportedHashTypes() const
|
||||
{
|
||||
QStringList list;
|
||||
list += "sha1";
|
||||
list += "sha0";
|
||||
list += "md5";
|
||||
return list;
|
||||
}
|
||||
|
||||
// MAC and Cipher types can go in here
|
||||
};
|
||||
\endcode
|
||||
|
||||
Note that InfoContext is itself a feature, so you have to add it to the createContext()
|
||||
method for the provider, as shown below:
|
||||
\code
|
||||
Context *createContext(const QString &type)
|
||||
{
|
||||
if ( type == "sha1" )
|
||||
return new opensslHashContext( EVP_sha1(), this, type);
|
||||
else if ( type == "sha0" )
|
||||
return new opensslHashContext( EVP_sha(), this, type);
|
||||
else if ( type == "md5" )
|
||||
return new opensslHashContext( EVP_md5(), this, type);
|
||||
else if ( type == "info" )
|
||||
return new opensslInfoContext( this );
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
\endcode
|
||||
|
||||
*/
|
||||
|
||||
/** \page providers Providers
|
||||
|
||||
%QCA works on the concept of a "provider". There is a limited
|
||||
internal provider (named "default"), but most of the work is
|
||||
done in plugin modules.
|
||||
|
||||
The logic to selection of a provider is fairly simple. The user can
|
||||
specify a provider name - if that name exists, and the provider supports
|
||||
the requested feature, then the named provider is used. If that
|
||||
didn't work, then the available plugins are searched (based on a
|
||||
priority order) for the requested feature. If that doesn't work,
|
||||
then the default provider is searched for the requested feature.
|
||||
|
||||
So the only way to get the default provider is to either have no other support
|
||||
whatsoever, or to specify the default provider directly (this goes for the
|
||||
algorithm constructors as well as setGlobalRNG()).
|
||||
|
||||
You can add your own provider in two ways - as a shared object plugin,
|
||||
and as a part of the client code.
|
||||
|
||||
The shared object plugin needs to be able to be found using the
|
||||
built-in scan logic - this normally means you need to install it into
|
||||
the plugins/crypto subdirectory within the directory that Qt is
|
||||
installed to. This will make it available for all applications.
|
||||
|
||||
If you have a limited application domain (such as a specialist
|
||||
algorithm, or a need to be bug-compatible), you may find it easier to
|
||||
create a client-side provider, and add it using the
|
||||
QCA::insertProvider call. There is an example of this - see
|
||||
<a href="aes-cmac_8cpp-example.html">the AES-CMAC example</a>.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(Qt@QT_MAJOR_VERSION@Core)
|
||||
|
||||
if (@QT_MAJOR_VERSION@ EQUAL 6 AND NOT @BUILD_SHARED_LIBRARIES@)
|
||||
find_dependency(Qt@QT_MAJOR_VERSION@Core5Compat)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET @QCA_LIB_NAME@)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@QCA_CONFIG_NAME_BASE@Targets.cmake")
|
||||
endif()
|
||||
|
||||
set(Qca_LIBRARY @QCA_LIB_NAME@)
|
||||
@@ -0,0 +1,113 @@
|
||||
Qt Cryptographic Architecture (QCA)
|
||||
-----------------------------------
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
QCA is a library that provides an easy API for a range of cryptographic
|
||||
features, including SSL/TLS, X.509 certificates, SASL, OpenPGP, smartcards,
|
||||
and much more.
|
||||
|
||||
Functionality is supplied via plugins. This is useful for avoiding
|
||||
dependence on a particular crypto library and makes upgrading easier,
|
||||
as there is no need to recompile your application when adding or
|
||||
upgrading a crypto plugin.
|
||||
|
||||
In order for QCA to be of much use, you'll want to install some plugins.
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
For installation or compiling instructions, see the INSTALL file.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
This library is licensed under the Lesser GNU General Public License. See
|
||||
the COPYING file for more information.
|
||||
|
||||
|
||||
History
|
||||
-------
|
||||
|
||||
QCA was originally created to support the security needs of the
|
||||
Psi XMPP/Jabber client project ( http://psi-im.org/ ).
|
||||
|
||||
|
||||
Old Changes list
|
||||
----------------
|
||||
|
||||
New in 2.1.0
|
||||
- Ported to Qt5 (Qt4 also supported)
|
||||
- New building system. CMake instead of qmake
|
||||
- Added CTR symetric cipher support to qca core
|
||||
- Added no padding encryption algorithm to qca core
|
||||
- qcatool2 renamed to qcatool
|
||||
- fixed crash in qcatool when only options provided on command line without any commands
|
||||
- Use plugins installation path as hard-coded runtime plugins search path
|
||||
- Added new functiion pluginPaths
|
||||
- Added functions to get runtime QCA version
|
||||
- Fixed 'no watch file' warnings in FileWatch
|
||||
- Added EME_PKCS1v15_SSL Encryption Algorithm
|
||||
- New implementation of SafeTimer to prevent crashes
|
||||
- Updated certificates for unittests
|
||||
- RSA Keys are permutable, can encrypt with private and decrypt with public
|
||||
- Add unloadProvider() function for symmetry with insertProvider()
|
||||
- Overloaded "makeKey" to derive a password depending on a time factor
|
||||
- Remove pointer to deinit() routine from QCoreApplication at deinitialization
|
||||
- Fix a couple of crashes where all plugins might not be available
|
||||
- Fix operating on keys with unrelated expired subkeys
|
||||
- Fixed timers in Synchronizer class
|
||||
- Dropped randomunittest
|
||||
- Fixed many unittests
|
||||
- qca-gnupg: internal refactoring
|
||||
- qca-gnupg: try both gpg and gpg2 to find gnupg executable
|
||||
- qca-gnupg: fixed some encodings problem
|
||||
- qca-ossl: no DSA_* dl groups in FIPS specification
|
||||
- qca-ossl: added missed signatures to CRLContext
|
||||
- qca-ossl: fixed certs time zone
|
||||
- qca-nss: fixed KeyLenght for Cipher
|
||||
- qca-botan: fixed getting result size for ciphers
|
||||
|
||||
New in 2.0.3
|
||||
- Bugfix release, forward and backward compatible with 2.0.x
|
||||
- Fix compilation when using Qt/Windows SDK
|
||||
|
||||
New in 2.0.2
|
||||
- Bugfix release, forward and backward compatible with 2.0.x
|
||||
- Fix compatibility with Qt 4.5 when QCA::Initializer appears before QApp
|
||||
- Don't convert to secure memory when Hash::update(QByteArray) is used
|
||||
- Use configure.exe instead of configwin.bat
|
||||
|
||||
New in 2.0.1
|
||||
- Bugfix release, forward and backward compatible with 2.0.x
|
||||
- Ability to build as a Mac framework (and build this way by default)
|
||||
- On non-Mac Unix, the pkgconfig file is always qca2.pc, even in debug mode
|
||||
- Certificates containing wildcards are now matched properly
|
||||
- DirWatch/FileWatch now work
|
||||
- Keystore writes now work
|
||||
- Don't delete objects in their event handler (prevents Qt 4.4 warnings)
|
||||
- Fix potential hang with TLS in server mode
|
||||
- Windows version can be configured/installed using paths with spaces
|
||||
|
||||
|
||||
Old Developer list
|
||||
------------------
|
||||
|
||||
Project Lead/Maintainer (2003-2012):
|
||||
Justin Karneges <justin@affinix.com>
|
||||
(March 2007 - August 2007 under Barracuda Networks employment)
|
||||
|
||||
Development, Documentation, Unittests (2004-2009):
|
||||
Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Development (2013-2017)
|
||||
Ivan Romanov <drizt@land.ru>
|
||||
|
||||
Special Thanks:
|
||||
Portugal Telecom (SAPO division), for sponsorship
|
||||
Alon Bar-Lev, for smart card and design assistance
|
||||
Jack Lloyd, for Botan and X.509 mentoring
|
||||
L. Peter Deutsch, for the public domain MD5 implementation
|
||||
Steve Reid, for the public domain SHA1 implementation
|
||||
Jason Kim, for the CMS Signer graphics
|
||||
@@ -0,0 +1,14 @@
|
||||
We introduced clang-format mandatory usage in September 2020.
|
||||
If you want git blame to ignore the revision in which we did the mass change you can do
|
||||
git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
on your clone
|
||||
|
||||
To get the clang-format warnings locally instead at CI time we recommend you
|
||||
to copy the hooks/pre-commit to your .git
|
||||
cp hooks/pre-commit .git/hooks/
|
||||
|
||||
We are using clang-format 19 on CI. Unfortunately clang-format is not totally
|
||||
compatible with older versions of itself. If CI gives you trouble but your local
|
||||
clang-format disagrees, just apply the changes suggested by CI and then commit
|
||||
with the --no-verify flag. If you get stuck, don't hesitate to ask the reviewer
|
||||
to help and they will reformat your commits :)
|
||||
@@ -0,0 +1,104 @@
|
||||
* 2.2.0
|
||||
- New plugin qca-gpgme to replace current qca-gnupg. qca-gnupg requires
|
||||
to have gpg binary which can be any 1.4.x or 2.x. Them behaviour is
|
||||
different. gpg2 requires gpg-agent to ask user for passphrase. No
|
||||
correct way to check that key requires passphrase.
|
||||
|
||||
- Add an interface for gnupg to handle keys (create and remove, maybe
|
||||
something else).
|
||||
|
||||
- Add ECDSA support to qca-ossl plugin
|
||||
|
||||
-- Obsoletes
|
||||
* 2.0.4
|
||||
handle mac universal builds for arches besides x86 and ppc (e.g. x86_64)
|
||||
use rpath?
|
||||
cms example: User.pem expired. probably other certs need regen also?
|
||||
fix publickeyexample. it encrypts with cms, decrypts with raw rsa??
|
||||
qcatool: instead of printing full usage at every wrong turn, we might try
|
||||
to print something closer to the context
|
||||
publickey: use_asker_fallback should use ErrorPassphrase
|
||||
cmssigner: convert path separators in field when loading a pkcs11 module
|
||||
cert: better makeFriendlyName diffing for key usage (consider OIDs) ?
|
||||
cert unit tests need to test wildcards!!
|
||||
consider a more readable implementation of qca_securelayer logic using the
|
||||
object-sessions concept (write the blog first :))
|
||||
vs2005: big_io.obj : warning LNK4221: no public symbols found; archive
|
||||
member will be inaccessible
|
||||
|
||||
* 2.1.0
|
||||
CertificateAuthority constructor provider argument should have default value
|
||||
CertificateAuthority::createCertificate declared but not implemented!
|
||||
CertificateCollection: ability to remove certs/crls?
|
||||
pull kwallet's SecureBuffer?
|
||||
|
||||
* possibilities for the future:
|
||||
abort if feature requested but not found, rather than crashing
|
||||
examples: bigint, pgp, tls with smartcards (reuse ssltest?), more?
|
||||
unittests: pgp enc/dec, cms, sasl, tls, filewatch/dirwatch
|
||||
complete pkits unittests
|
||||
don't expose windows.h in qpipe.h, find another way to get HANDLE
|
||||
cert: ability to get a list of all validation errors, rather than just one
|
||||
the securelayer system doesn't respond to the app if processed data yields
|
||||
no events (an example of this would be a partially-received encrypted
|
||||
payload). since securelayer is async, an app could end up waiting for
|
||||
a response that is never going to come. the current workaround is to wait
|
||||
some event cycles and then give up (see the saslclient example). the fix
|
||||
is probably to make the securelayer system always respond to the app once
|
||||
data has been processed, whether or not the processing yields anything
|
||||
interesting yet.
|
||||
find a better way for a provider to automatically promote itself? (see
|
||||
qca-wingss)
|
||||
can we ever get SASL::NoAuthzid?
|
||||
make it possible to change GNUPGHOME and have qca-gnupg respect the change?
|
||||
see pgpunittest.
|
||||
why is Logger a QObject?
|
||||
it seems strange that Logger friends QCA::Global...
|
||||
operator==,!= for PGPKey?
|
||||
gss/kerberos interface?
|
||||
spnego?
|
||||
make sure it is possible to add new public key types (e.g. ECC). right now
|
||||
it looks like qca_publickey is hard-coded sometimes (such as canVerify()
|
||||
returning true only for RSA and DSA public keys, no way to override this
|
||||
in a plugin...)
|
||||
if you store a typical self-signed cert into the system store as a way to
|
||||
trust it, does that make you vulnerable to the cert signing other certs?
|
||||
make distinction between invalid ca and ca not found
|
||||
make distinction between depth 0 self-signed and self-signed in chain
|
||||
use Q_PROPERTY on some things (e.g. TLS::compressionEnabled) ?
|
||||
signRequest should allow specifying the serial number
|
||||
qca-ossl: give credit to tim and "openssl" itself, not just eric?
|
||||
tls: should there be an explicit property for requesting a certificate in
|
||||
server mode? right now qca-ossl simply always asks for a cert
|
||||
publickey: ability to compare keys without IO support
|
||||
provider: separate public and private keys into two classes to enable
|
||||
delegation easier. the public part should not be implemented by most
|
||||
providers.
|
||||
provider: make it possible to support RSA and DSA keys without implementing
|
||||
a separate class for each.
|
||||
qcatool: streaming securemessage operations
|
||||
access to list of known cached certs, similar to how cmssigner works?
|
||||
securemessage: ability to know which key has performed a decrypt?
|
||||
emsa3Encode: implement in provider instead of qca?
|
||||
OCSP
|
||||
securelayer: ability to specify how much to read, rather than just read all
|
||||
tls ocsp stapling
|
||||
tls: pgp, psk auth ?
|
||||
internally managed intermediate object storage
|
||||
securemessage: algorithm selection for cms/pgp (and use SecurityLevel?)
|
||||
tls: renegotiation
|
||||
Key wrapping - RFC3217 and RFC3394
|
||||
quoted-printable TextFilter
|
||||
keygen on smart cards
|
||||
keystore: symmetric keys, arbitrary app data
|
||||
cert: any other fields we don't support but might be used out there
|
||||
cert: support for arbitrary extensions?
|
||||
cms: fine-grained control over smime attribs
|
||||
convertToPublic should return a publickey rather than change itself
|
||||
providers for:
|
||||
Mozilla NSS
|
||||
Windows CryptoAPI
|
||||
Linux kernel crypto support, if the userspace API ever gets sorted out
|
||||
Intel Performance Primatives library
|
||||
Botan: partly implemented
|
||||
EGD / EGADS: implement for Random support
|
||||
@@ -0,0 +1,5 @@
|
||||
rootcerts.pem is created by qca/tools/mozcerts
|
||||
|
||||
File: https://hg.mozilla.org/mozilla-central/log/tip/security/nss/lib/ckfw/builtins/certdata.txt
|
||||
Date: June 24, 2024
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,40 @@
|
||||
# - Try to find the pkcs11-helper library
|
||||
# Once done this will define
|
||||
#
|
||||
# PKCS11H_FOUND - system has pkcs11-helper
|
||||
# PKCS11H_INCLUDE_DIRS - the pkcs11-helper include directories
|
||||
# PKCS11H_LDFLAGS - Link to these to use pkcs11-helper
|
||||
# PKCS11H_CFLAGS_OTHER - Compiler switches required for using pkcs11-helper
|
||||
#
|
||||
# Copyright (c) 2006, Laurent Montel, <montel@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
# pkcs11-helper can be found at http://www.opensc-project.org/pkcs11-helper
|
||||
#
|
||||
|
||||
if(PKCS11H_INCLUDE_DIRS AND PKCS11H_LDFLAGS)
|
||||
|
||||
# in cache already
|
||||
SET(PKCS11H_FOUND TRUE)
|
||||
|
||||
else()
|
||||
if(NOT WIN32)
|
||||
find_package(PkgConfig)
|
||||
pkg_search_module(PKCS11H libpkcs11-helper-1)
|
||||
endif()
|
||||
|
||||
if (PKCS11H_FOUND)
|
||||
if (NOT Pkcs11Helper_FIND_QUIETLY)
|
||||
message(STATUS "Found pkcs11-helper: ${PKCS11H_LDFLAGS}")
|
||||
endif()
|
||||
else()
|
||||
if (Pkcs11Helper_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could NOT find pkcs11-helper")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(PKCS11H_INCLUDE_DIRS PKCS11H_LDFLAGS PKCS11H_CFLAGS_OTHER)
|
||||
|
||||
endif()
|
||||
@@ -0,0 +1,41 @@
|
||||
# - Try to find the sasl2 directory library
|
||||
# Once done this will define
|
||||
#
|
||||
# SASL2_FOUND - system has SASL2
|
||||
# SASL2_INCLUDE_DIR - the SASL2 include directory
|
||||
# SASL2_LIBRARIES - The libraries needed to use SASL2
|
||||
#
|
||||
# Copyright (c) 2006, Laurent Montel, <montel@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
|
||||
if (SASL2_INCLUDE_DIR)
|
||||
# Already in cache, be silent
|
||||
set(SASL2_FIND_QUIETLY TRUE)
|
||||
endif()
|
||||
|
||||
FIND_PATH(SASL2_INCLUDE_DIR sasl/sasl.h)
|
||||
|
||||
FIND_LIBRARY(SASL2_LIBRARIES NAMES sasl2)
|
||||
|
||||
|
||||
if (SASL2_INCLUDE_DIR AND SASL2_LIBRARIES)
|
||||
set(SASL2_FOUND TRUE)
|
||||
endif()
|
||||
|
||||
|
||||
if (SASL2_FOUND)
|
||||
if (NOT Sasl2_FIND_QUIETLY)
|
||||
message(STATUS "Found Sasl2: ${SASL2_LIBRARIES}")
|
||||
endif()
|
||||
else()
|
||||
if (Sasl2_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find sasl2 libraries")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
MARK_AS_ADVANCED(SASL2_INCLUDE_DIR SASL2_LIBRARIES)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
MACRO(SETUP_QT_DIRS)
|
||||
if(BUILD_WITH_QT6)
|
||||
GET_TARGET_PROPERTY(QMAKE_EXECUTABLE Qt6::qmake LOCATION)
|
||||
else()
|
||||
GET_TARGET_PROPERTY(QMAKE_EXECUTABLE ${Qt5Core_QMAKE_EXECUTABLE} LOCATION)
|
||||
endif()
|
||||
EXEC_PROGRAM( ${QMAKE_EXECUTABLE} ARGS "-query QT_INSTALL_LIBS" OUTPUT_VARIABLE QT_LIBRARY_DIR )
|
||||
EXEC_PROGRAM( ${QMAKE_EXECUTABLE} ARGS "-query QT_INSTALL_PREFIX" OUTPUT_VARIABLE QT_PREFIX_DIR )
|
||||
EXEC_PROGRAM( ${QMAKE_EXECUTABLE} ARGS "-query QT_INSTALL_PLUGINS" OUTPUT_VARIABLE QT_PLUGINS_DIR )
|
||||
EXEC_PROGRAM( ${QMAKE_EXECUTABLE} ARGS "-query QT_INSTALL_BINS" OUTPUT_VARIABLE QT_BINARY_DIR )
|
||||
EXEC_PROGRAM( ${QMAKE_EXECUTABLE} ARGS "-query QT_INSTALL_HEADERS" OUTPUT_VARIABLE QT_HEADERS_DIR )
|
||||
EXEC_PROGRAM( ${QMAKE_EXECUTABLE} ARGS "-query QT_INSTALL_DOCS" OUTPUT_VARIABLE QT_DOC_DIR )
|
||||
EXEC_PROGRAM( ${QMAKE_EXECUTABLE} ARGS "-query QT_INSTALL_DATA" OUTPUT_VARIABLE QT_DATA_DIR )
|
||||
EXEC_PROGRAM( ${QMAKE_EXECUTABLE} ARGS "-query QT_HOST_DATA" OUTPUT_VARIABLE QT_ARCHDATA_DIR )
|
||||
SET( QT_MKSPECS_DIR "${QT_ARCHDATA_DIR}/mkspecs" )
|
||||
ENDMACRO(SETUP_QT_DIRS)
|
||||
|
||||
macro(set_enabled_plugin PLUGIN ENABLED)
|
||||
# To nice looks
|
||||
if(ENABLED)
|
||||
set(ENABLED "on")
|
||||
else()
|
||||
set(ENABLED "off")
|
||||
endif()
|
||||
set(WITH_${PLUGIN}_PLUGIN_INTERNAL ${ENABLED} CACHE INTERNAL "")
|
||||
endmacro(set_enabled_plugin)
|
||||
|
||||
macro(enable_plugin PLUGIN)
|
||||
set_enabled_plugin(${PLUGIN} "on")
|
||||
endmacro(enable_plugin)
|
||||
|
||||
macro(disable_plugin PLUGIN)
|
||||
set_enabled_plugin(${PLUGIN} "off")
|
||||
endmacro(disable_plugin)
|
||||
|
||||
# it used to build examples and tools
|
||||
macro(target_link_qca_libraries TARGET)
|
||||
# Link with QCA library
|
||||
target_link_libraries(${TARGET} ${QCA_LIB_NAME})
|
||||
|
||||
# Statically link with all enabled QCA plugins
|
||||
if(STATIC_PLUGINS)
|
||||
target_link_libraries(${TARGET} ${QT_QTCORE_LIB_DEPENDENCIES})
|
||||
foreach(PLUGIN IN LISTS PLUGINS)
|
||||
# Check plugin for enabled
|
||||
if(WITH_${PLUGIN}_PLUGIN_INTERNAL)
|
||||
target_link_libraries(${TARGET} qca-${PLUGIN})
|
||||
endif()
|
||||
endforeach(PLUGIN)
|
||||
endif()
|
||||
endmacro(target_link_qca_libraries)
|
||||
|
||||
# it used to build unittests
|
||||
macro(target_link_qca_test_libraries TARGET)
|
||||
target_link_qca_libraries(${TARGET})
|
||||
target_link_libraries(${TARGET} Qt${QT_MAJOR_VERSION}::Test)
|
||||
endmacro(target_link_qca_test_libraries)
|
||||
|
||||
# it used to build unittests
|
||||
macro(add_qca_test TARGET DESCRIPTION)
|
||||
add_test(NAME "${DESCRIPTION}"
|
||||
WORKING_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
|
||||
COMMAND "${TARGET}")
|
||||
endmacro(add_qca_test)
|
||||
|
||||
macro(install_pdb TARGET INSTALL_PATH)
|
||||
if(MSVC)
|
||||
install(FILES $<TARGET_PDB_FILE:${TARGET}> DESTINATION ${INSTALL_PATH} CONFIGURATIONS Debug)
|
||||
install(FILES $<TARGET_PDB_FILE:${TARGET}> DESTINATION ${INSTALL_PATH} CONFIGURATIONS RelWithDebInfo)
|
||||
endif()
|
||||
endmacro(install_pdb)
|
||||
|
||||
macro(normalize_path PATH)
|
||||
get_filename_component(${PATH} "${${PATH}}" ABSOLUTE)
|
||||
# Strip trailing slashes
|
||||
string(REGEX REPLACE "/+$" "" PATH ${PATH})
|
||||
endmacro()
|
||||
@@ -0,0 +1,26 @@
|
||||
cmake_minimum_required(VERSION 2.6.0)
|
||||
|
||||
cmake_policy(SET CMP0007 OLD)
|
||||
|
||||
if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||
message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
|
||||
endif()
|
||||
|
||||
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
|
||||
string(REGEX REPLACE "\n" ";" files "${files}")
|
||||
list(REVERSE files)
|
||||
foreach (file ${files})
|
||||
message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
|
||||
if (EXISTS "$ENV{DESTDIR}${file}")
|
||||
execute_process(
|
||||
COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}"
|
||||
OUTPUT_VARIABLE rm_out
|
||||
RESULT_VARIABLE rm_retval
|
||||
)
|
||||
if(NOT ${rm_retval} EQUAL 0)
|
||||
message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
|
||||
endif()
|
||||
endforeach(file)
|
||||
@@ -0,0 +1,26 @@
|
||||
QCA_INCDIR = @CRYPTO_PRF_RELATIVE_PATH@@QCA_INCLUDE_INSTALL_DIR@
|
||||
QCA_LIBDIR = @CRYPTO_PRF_RELATIVE_PATH@@QCA_LIBRARY_INSTALL_DIR@
|
||||
|
||||
CONFIG *= qt
|
||||
|
||||
LINKAGE =
|
||||
|
||||
exists($$QCA_LIBDIR/@QCA_LIB_NAME@.framework) {
|
||||
QMAKE_CXXFLAGS += -F$$QCA_LIBDIR
|
||||
LIBS *= -F$$QCA_LIBDIR
|
||||
INCLUDEPATH += $$QCA_LIBDIR/@QCA_LIB_NAME@.framework/Headers
|
||||
LINKAGE = -framework @QCA_LIB_NAME@
|
||||
}
|
||||
|
||||
# else, link normally
|
||||
isEmpty(LINKAGE) {
|
||||
INCLUDEPATH += $$QCA_INCDIR/QtCrypto
|
||||
LIBS += -L$$QCA_LIBDIR
|
||||
LINKAGE = -l@QCA_LIB_NAME@
|
||||
CONFIG(debug, debug|release) {
|
||||
windows:LINKAGE = -l@QCA_LIB_NAME@d
|
||||
mac:LINKAGE = -l@QCA_LIB_NAME@_debug
|
||||
}
|
||||
}
|
||||
|
||||
LIBS += $$LINKAGE
|
||||
@@ -0,0 +1,3 @@
|
||||
### KApiDox Project-specific Overrides File
|
||||
|
||||
FILE_PATTERNS += *.doco
|
||||
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,313 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="744.09448819"
|
||||
height="1052.3622047"
|
||||
id="svg2"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.45.1"
|
||||
sodipodi:docbase="/home/bradh"
|
||||
sodipodi:docname="qca-arch.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||
<defs
|
||||
id="defs4">
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Lend"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="Arrow2Lend"
|
||||
style="overflow:visible;">
|
||||
<path
|
||||
id="path11975"
|
||||
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(1.1) rotate(180) translate(1,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Lstart"
|
||||
orient="auto"
|
||||
refY="0.0"
|
||||
refX="0.0"
|
||||
id="Arrow2Lstart"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path11972"
|
||||
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round"
|
||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||
transform="scale(1.1) translate(1,0)" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
gridtolerance="10000"
|
||||
guidetolerance="10"
|
||||
objecttolerance="10"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.3492222"
|
||||
inkscape:cx="475.84893"
|
||||
inkscape:cy="507.33883"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="729"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect2160"
|
||||
width="168.69115"
|
||||
height="233.32437"
|
||||
x="286.70642"
|
||||
y="429.78156" />
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect3134"
|
||||
width="185.59149"
|
||||
height="29.423277"
|
||||
x="503.70898"
|
||||
y="434.30725" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:12px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="368.11102"
|
||||
y="497.88052"
|
||||
id="text4105"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4107"
|
||||
x="368.11102"
|
||||
y="497.88052">Core QCA library</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="368.11102"
|
||||
y="512.88052"
|
||||
id="tspan4117">(libqca)</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:12px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="587.36017"
|
||||
y="452.32239"
|
||||
id="text7034"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7036"
|
||||
x="587.36017"
|
||||
y="452.32239">GnuPG-based plugin</tspan></text>
|
||||
<rect
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.87437826px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="rect7044"
|
||||
width="168.88092"
|
||||
height="157.35104"
|
||||
x="69.640457"
|
||||
y="429.9487" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="115.46945"
|
||||
y="471.40652"
|
||||
id="text7064"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7066"
|
||||
x="115.46945"
|
||||
y="471.40652">Application</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:0.9484126;stroke-opacity:1"
|
||||
id="rect7068"
|
||||
width="168.81155"
|
||||
height="42.521194"
|
||||
x="69.672821"
|
||||
y="587.35339" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:12px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="151.22769"
|
||||
y="606.68561"
|
||||
id="text7070"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7072"
|
||||
x="151.22769"
|
||||
y="606.68561">Client-side provider</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="151.22769"
|
||||
y="621.68561"
|
||||
id="tspan7074">(if used)</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend);stroke-opacity:1"
|
||||
d="M 501.70806,449.88179 C 457.68495,449.88179 457.68495,449.88179 457.68495,449.88179"
|
||||
id="path10980" />
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect7076"
|
||||
width="184.11676"
|
||||
height="29.43088"
|
||||
x="503.70898"
|
||||
y="480.47388" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:12px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="589.71906"
|
||||
y="496.26929"
|
||||
id="text7078"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7080"
|
||||
x="589.71906"
|
||||
y="496.26929">OpenSSL-based plugin</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend);stroke-opacity:1"
|
||||
d="M 501.70806,496.03305 C 457.68495,496.03305 457.68495,496.03305 457.68495,496.03305"
|
||||
id="path12151" />
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect7082"
|
||||
width="184.85413"
|
||||
height="29.427074"
|
||||
x="503.70898"
|
||||
y="529.39282" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:12px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="591.20135"
|
||||
y="544.44519"
|
||||
id="text7084"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7086"
|
||||
x="591.20135"
|
||||
y="544.44519">Smartcard (PKCS-11) plugin</tspan></text>
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect8067"
|
||||
width="184.11676"
|
||||
height="29.43088"
|
||||
x="514.92169"
|
||||
y="599.1095" />
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect7094"
|
||||
width="184.11676"
|
||||
height="29.43088"
|
||||
x="508.84338"
|
||||
y="592.2049" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend);stroke-opacity:1"
|
||||
d="M 501.70806,542.67659 C 457.68495,542.67659 457.68495,542.67659 457.68495,542.67659"
|
||||
id="path12153" />
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect7088"
|
||||
width="184.11676"
|
||||
height="29.43088"
|
||||
x="503.70898"
|
||||
y="584.23737" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:12px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="589.34845"
|
||||
y="600.03278"
|
||||
id="text7090"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7092"
|
||||
x="589.34845"
|
||||
y="600.03278">Other plugins</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend);stroke-opacity:1"
|
||||
d="M 501.70806,598.75366 C 457.68495,598.75366 457.68495,598.75366 457.68495,598.75366"
|
||||
id="path12155" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.03144789px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend);stroke-opacity:1"
|
||||
d="M 286.07811,482.68206 C 239.0548,482.68206 239.0548,482.68206 239.0548,482.68206"
|
||||
id="path12157" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.03144789px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend);stroke-opacity:1"
|
||||
d="M 286.07811,607.93831 C 239.0548,607.93831 239.0548,607.93831 239.0548,607.93831"
|
||||
id="path13130" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="-536.06805"
|
||||
y="484.35336"
|
||||
id="text13137"
|
||||
transform="matrix(0,-1,1,0,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan13139"
|
||||
x="-536.06805"
|
||||
y="484.35336">---------Provider API---------</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="558.77972"
|
||||
y="-257.89087"
|
||||
id="text13141"
|
||||
transform="matrix(0,1,-1,0,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan13143"
|
||||
x="558.77972"
|
||||
y="-257.89087">Provider API</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;fill:#00ff00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="443.15756"
|
||||
y="-257.14972"
|
||||
id="text15083"
|
||||
transform="matrix(0,1,-1,0,0,0)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan15085"
|
||||
x="443.15756"
|
||||
y="-257.14972">User API</tspan></text>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:1.55542469;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect15087"
|
||||
width="109.70339"
|
||||
height="29.875454"
|
||||
x="311.65579"
|
||||
y="618.66467" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="317.21982"
|
||||
y="635.82587"
|
||||
id="text15089"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan15091"
|
||||
x="317.21982"
|
||||
y="635.82587">Internal Provider</tspan></text>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.87666082px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend);stroke-opacity:1"
|
||||
d="M 366.87805,584.8962 C 366.87805,618.38061 366.87805,618.38061 366.87805,618.38061"
|
||||
id="path15093" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
|
||||
x="366.72021"
|
||||
y="603.25006"
|
||||
id="text15095"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan15097"
|
||||
x="366.72021"
|
||||
y="603.25006"
|
||||
style="font-size:10px;text-align:center;text-anchor:middle">Provider API</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,27 @@
|
||||
find_package(Qt${QT_MAJOR_VERSION} REQUIRED Network)
|
||||
if(NOT BUILD_WITH_QT6)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}")
|
||||
endif()
|
||||
add_subdirectory(aes-cmac)
|
||||
add_subdirectory(base64test)
|
||||
add_subdirectory(certtest)
|
||||
add_subdirectory(ciphertest)
|
||||
add_subdirectory(cms)
|
||||
# this is not meant to be added, because it introduces
|
||||
# a dependency on QtGui.
|
||||
# add_subdirectory(cmssigner)
|
||||
add_subdirectory(eventhandlerdemo)
|
||||
add_subdirectory(hashtest)
|
||||
add_subdirectory(hextest)
|
||||
add_subdirectory(keyloader)
|
||||
add_subdirectory(mactest)
|
||||
add_subdirectory(md5crypt)
|
||||
add_subdirectory(providertest)
|
||||
add_subdirectory(publickeyexample)
|
||||
add_subdirectory(randomtest)
|
||||
add_subdirectory(rsatest)
|
||||
add_subdirectory(saslclient)
|
||||
add_subdirectory(saslserver)
|
||||
add_subdirectory(ssltest)
|
||||
add_subdirectory(sslservtest)
|
||||
add_subdirectory(tlssocket)
|
||||
@@ -0,0 +1,5 @@
|
||||
set(aes-cmac_bin_SRCS aes-cmac.cpp)
|
||||
|
||||
add_executable(aes-cmac ${aes-cmac_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(aes-cmac)
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
Copyright (C) 2006 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// QtCrypto has the declarations for all of QCA
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
class AESCMACContext : public QCA::MACContext
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AESCMACContext(QCA::Provider *p)
|
||||
: QCA::MACContext(p, QStringLiteral("cmac(aes)"))
|
||||
{
|
||||
}
|
||||
|
||||
// Helper to left shift an arbitrary length array
|
||||
// This is heavily based on the example in the I-D.
|
||||
QCA::SecureArray leftShift(const QCA::SecureArray &array)
|
||||
{
|
||||
// We create an output of the same size as the input
|
||||
QCA::SecureArray out(array.size());
|
||||
// We handle one byte at a time - this is the high bit
|
||||
// from the previous byte.
|
||||
int overflow = 0;
|
||||
|
||||
// work through each byte.
|
||||
for (int i = array.size() - 1; i >= 0; --i) {
|
||||
// do the left shift on this byte.
|
||||
out[i] = array[i] << 1;
|
||||
// make the low bit on this byte be the high bit
|
||||
// from the previous byte.
|
||||
out[i] |= overflow;
|
||||
// save the high bit for next time
|
||||
overflow = (array[i] & 0x80) ? 1 : 0;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Helper to XOR two arrays - must be same length
|
||||
QCA::SecureArray xorArray(const QCA::SecureArray &array1, const QCA::SecureArray &array2)
|
||||
{
|
||||
if (array1.size() != array2.size())
|
||||
// empty array
|
||||
return QCA::SecureArray();
|
||||
|
||||
QCA::SecureArray result(array1.size());
|
||||
|
||||
for (int i = 0; i < array1.size(); ++i)
|
||||
result[i] = array1[i] ^ array2[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void setup(const QCA::SymmetricKey &key) override
|
||||
{
|
||||
// We might not have a real key, since this can get called
|
||||
// from the constructor.
|
||||
if (key.size() == 0)
|
||||
return;
|
||||
|
||||
m_key = key;
|
||||
// Generate the subkeys
|
||||
QCA::SecureArray const_Zero(16);
|
||||
QCA::SecureArray const_Rb(16);
|
||||
const_Rb[15] = (char)0x87;
|
||||
|
||||
m_X = const_Zero;
|
||||
m_residual = QCA::SecureArray();
|
||||
|
||||
// Figure 2.2, step 1.
|
||||
QCA::Cipher aesObj(QStringLiteral("aes128"), QCA::Cipher::ECB, QCA::Cipher::DefaultPadding, QCA::Encode, key);
|
||||
QCA::SecureArray L = aesObj.process(const_Zero);
|
||||
|
||||
// Figure 2.2, step 2
|
||||
if (0 == (L[0] & 0x80))
|
||||
m_k1 = leftShift(L);
|
||||
else
|
||||
m_k1 = xorArray(leftShift(L), const_Rb);
|
||||
|
||||
// Figure 2.2, step 3
|
||||
if (0 == (m_k1[0] & 0x80))
|
||||
m_k2 = leftShift(m_k1);
|
||||
else
|
||||
m_k2 = xorArray(leftShift(m_k1), const_Rb);
|
||||
}
|
||||
|
||||
QCA::Provider::Context *clone() const override
|
||||
{
|
||||
return new AESCMACContext(*this);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
setup(m_key);
|
||||
}
|
||||
|
||||
QCA::KeyLength keyLength() const override
|
||||
{
|
||||
return QCA::KeyLength(16, 16, 1);
|
||||
}
|
||||
|
||||
// This is a bit different to the way the I-D does it,
|
||||
// to allow for multiple update() calls.
|
||||
void update(const QCA::MemoryRegion &a) override
|
||||
{
|
||||
QCA::SecureArray bytesToProcess = m_residual + a;
|
||||
int blockNum;
|
||||
// note that we don't want to do the last full block here, because
|
||||
// it needs special treatment in final().
|
||||
for (blockNum = 0; blockNum < ((bytesToProcess.size() - 1) / 16); ++blockNum) {
|
||||
// copy a block of data
|
||||
QCA::SecureArray thisBlock(16);
|
||||
for (int yalv = 0; yalv < 16; ++yalv)
|
||||
thisBlock[yalv] = bytesToProcess[blockNum * 16 + yalv];
|
||||
|
||||
m_Y = xorArray(m_X, thisBlock);
|
||||
|
||||
QCA::Cipher aesObj(
|
||||
QStringLiteral("aes128"), QCA::Cipher::ECB, QCA::Cipher::DefaultPadding, QCA::Encode, m_key);
|
||||
m_X = aesObj.process(m_Y);
|
||||
}
|
||||
// This can be between 1 and 16
|
||||
int numBytesLeft = bytesToProcess.size() - 16 * blockNum;
|
||||
// we copy the left over part
|
||||
m_residual.resize(numBytesLeft);
|
||||
for (int yalv = 0; yalv < numBytesLeft; ++yalv)
|
||||
m_residual[yalv] = bytesToProcess[blockNum * 16 + yalv];
|
||||
}
|
||||
|
||||
void final(QCA::MemoryRegion *out) override
|
||||
{
|
||||
QCA::SecureArray lastBlock;
|
||||
int numBytesLeft = m_residual.size();
|
||||
|
||||
if (numBytesLeft != 16) {
|
||||
// no full block, so we have to pad.
|
||||
m_residual.resize(16);
|
||||
m_residual[numBytesLeft] = (char)0x80;
|
||||
lastBlock = xorArray(m_residual, m_k2);
|
||||
} else {
|
||||
// this is a full block - no padding
|
||||
lastBlock = xorArray(m_residual, m_k1);
|
||||
}
|
||||
m_Y = xorArray(m_X, lastBlock);
|
||||
QCA::Cipher aesObj(QStringLiteral("aes128"), QCA::Cipher::ECB, QCA::Cipher::DefaultPadding, QCA::Encode, m_key);
|
||||
*out = aesObj.process(m_Y);
|
||||
}
|
||||
|
||||
protected:
|
||||
// first subkey
|
||||
QCA::SecureArray m_k1;
|
||||
// second subkey
|
||||
QCA::SecureArray m_k2;
|
||||
// main key
|
||||
QCA::SecureArray m_key;
|
||||
|
||||
// state
|
||||
QCA::SecureArray m_X;
|
||||
QCA::SecureArray m_Y;
|
||||
|
||||
// partial block that we can't do yet
|
||||
QCA::SecureArray m_residual;
|
||||
};
|
||||
|
||||
class ClientSideProvider : public QCA::Provider
|
||||
{
|
||||
public:
|
||||
int qcaVersion() const override
|
||||
{
|
||||
return QCA_VERSION;
|
||||
}
|
||||
|
||||
QString name() const override
|
||||
{
|
||||
return QStringLiteral("exampleClientSideProvider");
|
||||
}
|
||||
|
||||
QStringList features() const override
|
||||
{
|
||||
QStringList list;
|
||||
list += QStringLiteral("cmac(aes)");
|
||||
// you can add more features in here, if you have some.
|
||||
return list;
|
||||
}
|
||||
|
||||
Provider::Context *createContext(const QString &type) override
|
||||
{
|
||||
if (type == QLatin1String("cmac(aes)"))
|
||||
return new AESCMACContext(this);
|
||||
// else if (type == some other feature)
|
||||
// return some other context.
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// AES CMAC is a Message Authentication Code based on a block cipher
|
||||
// instead of the more normal keyed hash.
|
||||
// See RFC 4493 "The AES-CMAC Algorithm"
|
||||
class AES_CMAC : public QCA::MessageAuthenticationCode
|
||||
{
|
||||
public:
|
||||
AES_CMAC(const QCA::SymmetricKey &key = QCA::SymmetricKey(), const QString &provider = QString())
|
||||
: QCA::MessageAuthenticationCode(QStringLiteral("cmac(aes)"), key, provider)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
qDebug() << "This example shows AES CMAC";
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
if (!QCA::isSupported("aes128-ecb")) {
|
||||
qDebug() << "AES not supported!";
|
||||
}
|
||||
|
||||
if (QCA::insertProvider(new ClientSideProvider, 0))
|
||||
qDebug() << "Inserted our provider";
|
||||
else
|
||||
qDebug() << "our provider could not be added";
|
||||
|
||||
// We should check AES CMAC is supported before using it.
|
||||
if (!QCA::isSupported("cmac(aes)")) {
|
||||
qDebug() << "AES CMAC not supported!";
|
||||
} else {
|
||||
// create the required object
|
||||
AES_CMAC cmacObject;
|
||||
|
||||
// create the key
|
||||
QCA::SymmetricKey key(QCA::hexToArray(QStringLiteral("2b7e151628aed2a6abf7158809cf4f3c")));
|
||||
|
||||
// set the MAC to use the key
|
||||
cmacObject.setup(key);
|
||||
|
||||
QCA::SecureArray message =
|
||||
QCA::hexToArray(QStringLiteral("6bc1bee22e409f96e93d7e117393172a"
|
||||
"ae2d8a571e03ac9c9eb76fac45af8e51"
|
||||
"30c81c46a35ce411e5fbc1191a0a52ef"
|
||||
"f69f2445df4f9b17ad2b417be66c3710"));
|
||||
QCA::SecureArray message1(message);
|
||||
message1.resize(0);
|
||||
qDebug();
|
||||
qDebug() << "Message1: " << QCA::arrayToHex(message1.toByteArray());
|
||||
qDebug() << "Expecting: bb1d6929e95937287fa37d129b756746";
|
||||
qDebug() << "AES-CMAC: " << QCA::arrayToHex(cmacObject.process(message1).toByteArray());
|
||||
|
||||
cmacObject.clear();
|
||||
QCA::SecureArray message2(message);
|
||||
message2.resize(16);
|
||||
qDebug();
|
||||
qDebug() << "Message2: " << QCA::arrayToHex(message2.toByteArray());
|
||||
qDebug() << "Expecting: 070a16b46b4d4144f79bdd9dd04a287c";
|
||||
qDebug() << "AES-CMAC: " << QCA::arrayToHex(cmacObject.process(message2).toByteArray());
|
||||
|
||||
cmacObject.clear();
|
||||
QCA::SecureArray message3(message);
|
||||
message3.resize(40);
|
||||
qDebug();
|
||||
qDebug() << "Message3: " << QCA::arrayToHex(message3.toByteArray());
|
||||
qDebug() << "Expecting: dfa66747de9ae63030ca32611497c827";
|
||||
qDebug() << "AES-CMAC " << QCA::arrayToHex(cmacObject.process(message3).toByteArray());
|
||||
|
||||
cmacObject.clear();
|
||||
QCA::SecureArray message4(message);
|
||||
message4.resize(64);
|
||||
qDebug();
|
||||
qDebug() << "Message4: " << QCA::arrayToHex(message4.toByteArray());
|
||||
qDebug() << "Expecting: 51f0bebf7e3b9d92fc49741779363cfe";
|
||||
qDebug() << "AES-CMAC: " << QCA::arrayToHex(cmacObject.process(message4).toByteArray());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "aes-cmac.moc"
|
||||
@@ -0,0 +1,5 @@
|
||||
set(base64test_bin_SRCS base64test.cpp)
|
||||
|
||||
add_executable(base64test ${base64test_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(base64test)
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright (C) 2004-2005 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// QtCrypto has the declarations for all of QCA
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication(argc, argv);
|
||||
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
// we use the first argument as the data to encode
|
||||
// if an argument is provided. Use "hello" if no argument
|
||||
QByteArray arg; // empty array
|
||||
arg.append((argc >= 2) ? argv[1] : "hello");
|
||||
|
||||
// create our object, which does encoding by default
|
||||
// QCA::Base64 encoder(QCA::Encode); is equivalent
|
||||
QCA::Base64 encoder;
|
||||
|
||||
// This does the actual conversion (encoding).
|
||||
// You might prefer to use encoder.encode(); and have
|
||||
// it return a QCA::SecureArray, depending on your needs
|
||||
QString encoded = encoder.arrayToString(arg);
|
||||
|
||||
std::cout << arg.data() << " in base64 encoding is ";
|
||||
std::cout << encoded.toLatin1().data() << std::endl;
|
||||
|
||||
// This time, we'll create an object to decode base64. We
|
||||
// could also have reused the existing object, calling
|
||||
// clear(); and setup(QCA::Decode); on it.
|
||||
QCA::Base64 decoder(QCA::Decode);
|
||||
|
||||
// This time, we convert a QString into a QString
|
||||
QString decoded = decoder.decodeString(encoded);
|
||||
|
||||
std::cout << encoded.toLatin1().data() << " decoded from base64 is ";
|
||||
std::cout << decoded.toLatin1().data() << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
set(certtest_bin_SRCS certtest.cpp)
|
||||
|
||||
add_executable(certtest ${certtest_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(certtest)
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
Copyright (C) 2003 Justin Karneges <justin@affinix.com>
|
||||
Copyright (C) 2005 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
// dump out information about some part of the certificate
|
||||
// we use this same approach for information about the subject
|
||||
// of the certificate, and also about the issuer of the certificate
|
||||
static void dumpCertificateInfo(const QCA::CertificateInfo &info)
|
||||
{
|
||||
std::cout << " Organization: " << std::endl;
|
||||
|
||||
// Note that a single certificate can apply to more than one
|
||||
// organisation. QCA::Certificate is a multimap, so when you
|
||||
// ask for the values associated with a parameter, it returns
|
||||
// a list.
|
||||
QList<QString> orgInfoList = info.values(QCA::Organization);
|
||||
|
||||
// foreach() interates over each value in the list, and we dump
|
||||
// out each value. Note that is uncommon for a certificate to
|
||||
// actually contain multiple values for a single parameter.
|
||||
QString organization;
|
||||
foreach (organization, orgInfoList) {
|
||||
std::cout << " " << qPrintable(organization) << std::endl;
|
||||
}
|
||||
|
||||
std::cout << " Country: " << std::endl;
|
||||
// As above, however this shows a more compact way to represent
|
||||
// the iteration and output.
|
||||
foreach (QString country, info.values(QCA::Country)) { // clazy:exclude=container-anti-pattern
|
||||
std::cout << " " << qPrintable(country) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// This is just a convenience routine
|
||||
static void dumpSubjectInfo(const QCA::CertificateInfo &subject)
|
||||
{
|
||||
std::cout << "Subject: " << std::endl;
|
||||
|
||||
dumpCertificateInfo(subject);
|
||||
}
|
||||
|
||||
// This is just a convenience routine
|
||||
static void dumpIssuerInfo(const QCA::CertificateInfo &issuer)
|
||||
{
|
||||
std::cout << "Issuer: " << std::endl;
|
||||
|
||||
dumpCertificateInfo(issuer);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
// We need to ensure that we have certificate handling support
|
||||
if (!QCA::isSupported("cert")) {
|
||||
std::cout << "Sorry, no PKI certificate support" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// We are going to work with a number of certificates, and a
|
||||
// QList is a great template class for that
|
||||
QList<QCA::Certificate> certlist;
|
||||
|
||||
// We do two different cases - if we provide an argument, it is taken
|
||||
// as a filename to read the keys from. If there is no argument, we just
|
||||
// read from the system store certificates.
|
||||
if (argc >= 2) {
|
||||
// we are going to read the certificates in using a single call
|
||||
// which requires a CertificateCollection.
|
||||
QCA::CertificateCollection filecerts;
|
||||
// The conversion can be tested (although you don't have to) to find out if it
|
||||
// worked.
|
||||
QCA::ConvertResult importResult;
|
||||
// This imports all the PEM encoded certificates from the file specified as the argument
|
||||
// Note that you pass in a pointer to the result argument.
|
||||
filecerts = QCA::CertificateCollection::fromFlatTextFile(QFile::decodeName(argv[1]), &importResult);
|
||||
if (QCA::ConvertGood == importResult) {
|
||||
std::cout << "Import succeeded" << std::endl;
|
||||
// this turns the CertificateCollection into a QList of Certificate objects
|
||||
certlist = filecerts.certificates();
|
||||
}
|
||||
} else {
|
||||
// we have no arguments, so just use the system certificates
|
||||
if (!QCA::haveSystemStore()) {
|
||||
std::cout << "System certificates not available" << std::endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Similar to above, except we just want the system certificates
|
||||
QCA::CertificateCollection systemcerts = QCA::systemStore();
|
||||
|
||||
// this turns the CertificateCollection into a QList of Certificate objects
|
||||
certlist = systemcerts.certificates();
|
||||
}
|
||||
|
||||
std::cout << "Number of certificates: " << certlist.count() << std::endl;
|
||||
|
||||
QCA::Certificate cert;
|
||||
foreach (cert, certlist) {
|
||||
std::cout << "Serial Number:";
|
||||
// the serial number of the certificate is a QCA::BigInteger, but we can
|
||||
// just convert it to a string, and then output it.
|
||||
std::cout << qPrintable(cert.serialNumber().toString()) << std::endl;
|
||||
|
||||
// The subject information shows properties of who the certificate
|
||||
// applies to. See the convenience routines above.
|
||||
dumpSubjectInfo(cert.subjectInfo());
|
||||
|
||||
// The issuer information shows properties of who the certificate
|
||||
// was signed by. See the convenience routines above.
|
||||
dumpIssuerInfo(cert.issuerInfo());
|
||||
|
||||
// Test if the certificate can be used as a certificate authority
|
||||
if (cert.isCA()) {
|
||||
std::cout << "Is certificate authority" << std::endl;
|
||||
} else {
|
||||
std::cout << "Is not a certificate authority" << std::endl;
|
||||
}
|
||||
|
||||
// Test if the certificate is self-signed.
|
||||
if (cert.isSelfSigned()) {
|
||||
std::cout << "Self signed" << std::endl;
|
||||
} else {
|
||||
std::cout << "Is not self-signed!!!" << std::endl;
|
||||
}
|
||||
|
||||
// Certificate are only valid between specific dates. We can get the dates
|
||||
// (as a QDateTime) using a couple of calls
|
||||
std::cout << "Valid from " << qPrintable(cert.notValidBefore().toString());
|
||||
std::cout << ", until " << qPrintable(cert.notValidAfter().toString());
|
||||
std::cout << std::endl;
|
||||
|
||||
// You can get the certificate in PEM encoding with a simple toPEM() call
|
||||
std::cout << "PEM:" << std::endl;
|
||||
std::cout << qPrintable(cert.toPEM());
|
||||
std::cout << std::endl << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
set(ciphertest_bin_SRCS ciphertest.cpp)
|
||||
|
||||
add_executable(ciphertest ${ciphertest_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(ciphertest)
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright (C) 2003 Justin Karneges <justin@affinix.com>
|
||||
Copyright (C) 2005-2006 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// QtCrypto has the declarations for all of QCA
|
||||
#include <QtCrypto>
|
||||
#include <cstdio>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
// we use the first argument if provided, or
|
||||
// use "hello" if no arguments
|
||||
QCA::SecureArray arg = (argc >= 2) ? argv[1] : "hello";
|
||||
|
||||
// AES128 testing
|
||||
if (!QCA::isSupported("aes128-cbc-pkcs7"))
|
||||
printf("AES128-CBC not supported!\n");
|
||||
else {
|
||||
// Create a random key - you'd probably use one from another
|
||||
// source in a real application
|
||||
QCA::SymmetricKey key(16);
|
||||
|
||||
// Create a random initialisation vector - you need this
|
||||
// value to decrypt the resulting cipher text, but it
|
||||
// need not be kept secret (unlike the key).
|
||||
QCA::InitializationVector iv(16);
|
||||
|
||||
// create a 128 bit AES cipher object using Cipher Block Chaining (CBC) mode
|
||||
QCA::Cipher cipher(QStringLiteral("aes128"),
|
||||
QCA::Cipher::CBC,
|
||||
// use Default padding, which is equivalent to PKCS7 for CBC
|
||||
QCA::Cipher::DefaultPadding,
|
||||
// this object will encrypt
|
||||
QCA::Encode,
|
||||
key,
|
||||
iv);
|
||||
|
||||
// we use the cipher object to encrypt the argument we passed in
|
||||
// the result of that is returned - note that if there is less than
|
||||
// 16 bytes (1 block), then nothing will be returned - it is buffered
|
||||
// update() can be called as many times as required.
|
||||
QCA::SecureArray u = cipher.update(arg);
|
||||
|
||||
// We need to check if that update() call worked.
|
||||
if (!cipher.ok()) {
|
||||
printf("Update failed\n");
|
||||
}
|
||||
// output the results of that stage
|
||||
printf("AES128 encryption of %s is [%s]\n", arg.data(), qPrintable(QCA::arrayToHex(u.toByteArray())));
|
||||
|
||||
// Because we are using PKCS7 padding, we need to output the final (padded) block
|
||||
// Note that we should always call final() even with no padding, to clean up
|
||||
QCA::SecureArray f = cipher.final();
|
||||
|
||||
// Check if the final() call worked
|
||||
if (!cipher.ok()) {
|
||||
printf("Final failed\n");
|
||||
}
|
||||
// and output the resulting block. The ciphertext is the results of update()
|
||||
// and the result of final()
|
||||
printf("Final block for AES128 encryption is [0x%s]\n", qPrintable(QCA::arrayToHex(f.toByteArray())));
|
||||
|
||||
// re-use the Cipher t decrypt. We need to use the same key and
|
||||
// initialisation vector as in the encryption.
|
||||
cipher.setup(QCA::Decode, key, iv);
|
||||
|
||||
// Build a single cipher text array. You could also call update() with
|
||||
// each block as you receive it, if that is more useful.
|
||||
QCA::SecureArray cipherText = u.append(f);
|
||||
|
||||
// take that cipher text, and decrypt it
|
||||
QCA::SecureArray plainText = cipher.update(cipherText);
|
||||
|
||||
// check if the update() call worked
|
||||
if (!cipher.ok()) {
|
||||
printf("Update failed\n");
|
||||
}
|
||||
|
||||
// output results
|
||||
printf("Decryption using AES128 of [0x%s] is %s\n",
|
||||
qPrintable(QCA::arrayToHex(cipherText.toByteArray())),
|
||||
plainText.data());
|
||||
|
||||
// Again we need to call final(), to get the last block (with its padding removed)
|
||||
plainText = cipher.final();
|
||||
|
||||
// check if the final() call worked
|
||||
if (!cipher.ok()) {
|
||||
printf("Final failed\n");
|
||||
}
|
||||
|
||||
// output results
|
||||
printf("Final decryption block using AES128 is %s\n", plainText.data());
|
||||
// instead of update() and final(), you can do the whole thing
|
||||
// in one step, using process()
|
||||
printf("One step decryption using AES128: %s\n", QCA::SecureArray(cipher.process(cipherText)).data());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
set(cmsexample_bin_SRCS cmsexample.cpp)
|
||||
|
||||
add_executable(cmsexample ${cmsexample_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(cmsexample)
|
||||
@@ -0,0 +1,26 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEYjCCA0qgAwIBAgIBAjANBgkqhkiG9w0BAQQFADBvMQswCQYDVQQGEwJkZTEg
|
||||
MB4GA1UEChMXSW5zZWN1cmVUZXN0Q2VydGlmaWNhdGUxFzAVBgNVBAMTDkZvciBU
|
||||
ZXN0cyBPbmx5MSUwIwYJKoZIhvcNAQkBFhZpbnNlY3VyZUB0ZXN0Lmluc2VjdXJl
|
||||
MB4XDTAxMDgxNzA4MzIzOFoXDTA2MDgxNjA4MzIzOFoweDELMAkGA1UEBhMCZGUx
|
||||
IDAeBgNVBAoTF0luc2VjdXJlVGVzdENlcnRpZmljYXRlMSAwHgYDVQQDExdJbnNl
|
||||
Y3VyZSBVc2VyIFRlc3QgQ2VydDElMCMGCSqGSIb3DQEJARYWaW5zZWN1cmVAdGVz
|
||||
dC5pbnNlY3VyZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEArCM8+V7VH46Y
|
||||
+bMtgOaqFc8vCSBpSeL5hnHN3uwEH8/OqwKaO9hMP15lFpzEJOIPMVtOSLCg4dJy
|
||||
+eS3azL3/B+4qDVqbCgKSXDMKhBDKKw0TvfhN4tgfmvy0rpgMHYApfyRdewnvKgV
|
||||
YkI+sDwupmZJo87kuvPM2JzbV/DNAyMCAwEAAaOCAYIwggF+MAsGA1UdDwQEAwIE
|
||||
8DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwHQYDVR0OBBYEFIiefvcp
|
||||
cZ17KA82Gq5tANOd4arbMIGZBgNVHSMEgZEwgY6AFL9TQ4J40J7DgOUbZ8oFAN+5
|
||||
SIOloXOkcTBvMQswCQYDVQQGEwJkZTEgMB4GA1UEChMXSW5zZWN1cmVUZXN0Q2Vy
|
||||
dGlmaWNhdGUxFzAVBgNVBAMTDkZvciBUZXN0cyBPbmx5MSUwIwYJKoZIhvcNAQkB
|
||||
FhZpbnNlY3VyZUB0ZXN0Lmluc2VjdXJlggEAMCEGA1UdEQQaMBiBFmluc2VjdXJl
|
||||
QHRlc3QuaW5zZWN1cmUwIQYDVR0SBBowGIEWaW5zZWN1cmVAdGVzdC5pbnNlY3Vy
|
||||
ZTARBglghkgBhvhCAQEEBAMCBaAwPAYJYIZIAYb4QgENBC8WLVRoaXMgY2VydGlm
|
||||
aWNhdGUgd2FzIGlzc3VlZCBmb3IgdGVzdGluZyBvbmx5ITANBgkqhkiG9w0BAQQF
|
||||
AAOCAQEAeRBEcR/xp4pLH3VbQmTbZEGjVEBDxNAapsdIDrKB1ecA3JMhZDjweKc4
|
||||
MG5M+FQ5hcCT8kSi+6bL15BJRyyMB4727NRSC1i/2VkZmUGhhk3AR9UjsvrCC00D
|
||||
gPuHdQPrIxl9+CK26ypATizb5VapzmoBc2B/dWeVh+KJbEkgTudfFj98Dqn8kiUn
|
||||
bqbC3OMPa1uiez8oer8h6OAyOinmx0atjTqS5SOLI+2+p1lpMHMhodn4jgmd8Pms
|
||||
KQ0jMyA0ZQ1tozQXOw9VpRYegsm8LMq0emdfybxpwGbrCIIk7BXjBIDrhYbnb3GK
|
||||
blykzt4bqOeDtJuTgyBOS3Ldxqgfzg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,15 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgQCsIzz5XtUfjpj5sy2A5qoVzy8JIGlJ4vmGcc3e7AQfz86rApo7
|
||||
2Ew/XmUWnMQk4g8xW05IsKDh0nL55LdrMvf8H7ioNWpsKApJcMwqEEMorDRO9+E3
|
||||
i2B+a/LSumAwdgCl/JF17Ce8qBViQj6wPC6mZkmjzuS688zYnNtX8M0DIwIDAQAB
|
||||
AoGAUmPrY1BIofoWri1csj21Wwr3cF7bBkplNDx0VSaSMse8G437VHmKxSnhKJmQ
|
||||
KDcrHmydY51m469e6MzYHa8fkfwFiqRhBE7rSw9n0K1sqzU6fJYXgpxdV+GOQcKw
|
||||
v+Lu+o6ej4BGQ7woJZXOiC3GDiWu3rC91UNC9eC74yCGuAECQQDhd36m/M9Bjw+C
|
||||
pNi55oFGdOCaiL+OT9I3Gt2+7MIo4d2YGvhl8Nr3PC6bEq1DzoWiaE+qxE3JFZdp
|
||||
J2+F0fCjAkEAw3LsfrqVZH5HW57csAapmhA+oiCTP3cDDObQ3pKNi3sfYKq8u+aK
|
||||
93XAbw3+sPaWlQbVnQF+2fNxOZB9TqFLgQJALVw+Fdhs1hKzGySRGa1/Oq6O9Wgn
|
||||
qbjzYPS9wgid812lmFFswg2X7/+inL5AL5EgH5F+3atKvvbiFFzDuheHowJAHwiS
|
||||
cJTjv68lWXDiDbkhGOtXZprRhpQ5EcVciNqwSuNj2g+CJDu4+Q3jXj7Un/indEY/
|
||||
iMyB/c1fvwooO2H1AQJAEmlUKcfUMdMKbsnqcBfUcge3POdb06pXDpZ/+X0g2OBE
|
||||
1fTIQgPgP6y+Ucp/WCW6l2pEJtt26KI5kEp31WSbHA==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
Copyright (C) 2003 Justin Karneges <justin@affinix.com>
|
||||
Copyright (C) 2005-2006 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
// We need to ensure that we have certificate handling support
|
||||
if (!QCA::isSupported("cert")) {
|
||||
qWarning() << "Sorry, no PKI certificate support";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read in a public key cert
|
||||
// you could also build this using the fromPEMFile() method
|
||||
QCA::Certificate pubCert(QStringLiteral("User.pem"));
|
||||
if (pubCert.isNull()) {
|
||||
qWarning() << "Sorry, could not import public key certificate";
|
||||
return 1;
|
||||
}
|
||||
// We are building the certificate into a SecureMessageKey object, via a
|
||||
// CertificateChain
|
||||
QCA::SecureMessageKey secMsgKey;
|
||||
QCA::CertificateChain chain;
|
||||
chain += pubCert;
|
||||
secMsgKey.setX509CertificateChain(chain);
|
||||
|
||||
// build up a SecureMessage object, based on our public key certificate
|
||||
if (!QCA::isSupported("cms")) {
|
||||
qWarning() << "Sorry, no CMS support";
|
||||
return 1;
|
||||
}
|
||||
QCA::CMS cms;
|
||||
QCA::SecureMessage msg(&cms);
|
||||
msg.setRecipient(secMsgKey);
|
||||
|
||||
// Some plain text - we use the first command line argument if provided
|
||||
QByteArray plainText = (argc >= 2) ? argv[1] : "What do ya want for nuthin'";
|
||||
|
||||
// Now use the SecureMessage object to encrypt the plain text.
|
||||
msg.startEncrypt();
|
||||
msg.update(plainText);
|
||||
msg.end();
|
||||
// I think it is reasonable to wait for 1 second for this
|
||||
msg.waitForFinished(1000);
|
||||
|
||||
// check to see if it worked
|
||||
if (!msg.success()) {
|
||||
qWarning() << "Error encrypting: " << msg.errorCode();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// get the result
|
||||
QByteArray cipherText = msg.read();
|
||||
QCA::Base64 enc;
|
||||
qDebug() << "'" << plainText.data() << "' encrypts to (in base 64): ";
|
||||
qDebug() << enc.arrayToString(cipherText);
|
||||
qDebug() << "Message uses" << msg.hashName() << "hashing algorithm";
|
||||
qDebug();
|
||||
|
||||
// Show we can decrypt it with the private key
|
||||
|
||||
// Read in a private key
|
||||
QCA::PrivateKey privKey;
|
||||
QCA::ConvertResult convRes;
|
||||
QCA::SecureArray passPhrase = "start";
|
||||
privKey = QCA::PrivateKey::fromPEMFile(QStringLiteral("Userkey.pem"), passPhrase, &convRes);
|
||||
if (convRes != QCA::ConvertGood) {
|
||||
qWarning() << "Sorry, could not import Private Key";
|
||||
return 1;
|
||||
}
|
||||
|
||||
QCA::SecureMessageKey secMsgKey2;
|
||||
// needed?
|
||||
secMsgKey2.setX509CertificateChain(chain);
|
||||
secMsgKey2.setX509PrivateKey(privKey);
|
||||
QCA::SecureMessageKeyList privKeyList;
|
||||
privKeyList += secMsgKey2;
|
||||
|
||||
// build up a SecureMessage object, based on the private key
|
||||
// you could re-use the existing QCA::CMS object (cms), but
|
||||
// this example simulates encryption and one end, and decryption
|
||||
// at the other
|
||||
QCA::CMS anotherCms;
|
||||
anotherCms.setPrivateKeys(privKeyList);
|
||||
|
||||
QCA::SecureMessage msg2(&anotherCms);
|
||||
|
||||
msg2.startDecrypt();
|
||||
msg2.update(cipherText);
|
||||
msg2.end();
|
||||
|
||||
// I think it is reasonable to wait for 1 second for this
|
||||
msg2.waitForFinished(1000);
|
||||
|
||||
// check to see if it worked
|
||||
if (!msg2.success()) {
|
||||
qWarning() << "Error encrypting: " << msg2.errorCode();
|
||||
return 1;
|
||||
}
|
||||
|
||||
QCA::SecureArray plainTextResult = msg2.read();
|
||||
|
||||
qDebug() << enc.arrayToString(cipherText) << " (in base 64) decrypts to: " << plainTextResult.data();
|
||||
|
||||
if (msg2.wasSigned()) {
|
||||
qDebug() << "Message was signed at " << msg2.signer().timestamp();
|
||||
} else {
|
||||
qDebug() << "Message was not signed";
|
||||
}
|
||||
|
||||
qDebug() << "Message used" << msg2.hashName() << "hashing algorithm";
|
||||
|
||||
qDebug();
|
||||
|
||||
// Now we want to try a signature
|
||||
QByteArray text("Got your message");
|
||||
|
||||
// Re-use the CMS and SecureMessageKeyList objects from the decrypt...
|
||||
QCA::SecureMessage signing(&anotherCms);
|
||||
signing.setSigners(privKeyList);
|
||||
|
||||
signing.startSign(QCA::SecureMessage::Detached);
|
||||
signing.update(text);
|
||||
signing.end();
|
||||
|
||||
// I think it is reasonable to wait for 1 second for this
|
||||
signing.waitForFinished(1000);
|
||||
|
||||
// check to see if it worked
|
||||
if (!signing.success()) {
|
||||
qWarning() << "Error signing: " << signing.errorCode();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// get the result
|
||||
QByteArray signature = signing.signature();
|
||||
|
||||
qDebug() << "'" << text.data() << "', signature (converted to base 64), is: ";
|
||||
qDebug() << enc.arrayToString(signature);
|
||||
qDebug() << "Message uses" << signing.hashName() << "hashing algorithm";
|
||||
qDebug();
|
||||
|
||||
// Now we go back to the first CMS, and re-use that.
|
||||
QCA::SecureMessage verifying(&cms);
|
||||
|
||||
// You have to pass the signature to startVerify(),
|
||||
// and the message to update()
|
||||
verifying.startVerify(signature);
|
||||
verifying.update(text);
|
||||
verifying.end();
|
||||
|
||||
verifying.waitForFinished(1000);
|
||||
|
||||
// check to see if it worked
|
||||
if (!verifying.success()) {
|
||||
qWarning() << "Error verifying: " << verifying.errorCode();
|
||||
return 1;
|
||||
}
|
||||
|
||||
QCA::SecureMessageSignature sign;
|
||||
sign = verifying.signer();
|
||||
// todo: dump some data out about the signer
|
||||
|
||||
if (verifying.verifySuccess()) {
|
||||
qDebug() << "Message verified";
|
||||
} else {
|
||||
qDebug() << "Message failed to verify:" << verifying.errorCode();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
app icon (may need different sizes of this?) (a pen?)
|
||||
store icons
|
||||
system
|
||||
user
|
||||
application
|
||||
smart card
|
||||
pgp keyring
|
||||
unknown store/entry icon (can use the same icon for both i think)
|
||||
animation rather than text for keystore busy?
|
||||
animation for signing in progress (maybe just use an endless QProgressBar?)
|
||||
integrated logger window? also allow setting the log level
|
||||
prompter should not call entrylist unless necessary. right now it starts
|
||||
async mode on all stores, even if there is no prompting or anything going
|
||||
on. qcatool has this problem also
|
||||
report all 3 kinds of diagnostic text somewhere
|
||||
plugin: plugin dialog invoked from file menu ? also show provider versions
|
||||
keystore: invoked from keyselectdlg ?
|
||||
secmessage: "details" button on error dialog?
|
||||
@@ -0,0 +1,697 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "certitem.h"
|
||||
|
||||
#include "prompter.h"
|
||||
#include <QMessageBox>
|
||||
#include <QtCore>
|
||||
#include <QtCrypto>
|
||||
#include <QtGui>
|
||||
|
||||
typedef QMap<CertItemStore::IconType, QPixmap> CertItemIconset;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// MyPrompter
|
||||
//----------------------------------------------------------------------------
|
||||
class MyPrompter : public Prompter
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
QMap<QString, QCA::SecureArray> known;
|
||||
QMap<QString, QCA::SecureArray> maybe;
|
||||
|
||||
public:
|
||||
MyPrompter(QObject *parent = 0)
|
||||
: Prompter(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void fileSuccess(const QString &fileName)
|
||||
{
|
||||
if (maybe.contains(fileName)) {
|
||||
known[fileName] = maybe[fileName];
|
||||
maybe.remove(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
void fileFailed(const QString &fileName)
|
||||
{
|
||||
maybe.remove(fileName);
|
||||
known.remove(fileName);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual QCA::SecureArray knownPassword(const QCA::Event &event)
|
||||
{
|
||||
if (event.source() == QCA::Event::Data && !event.fileName().isEmpty())
|
||||
return known.value(event.fileName());
|
||||
else
|
||||
return QCA::SecureArray();
|
||||
}
|
||||
|
||||
virtual void userSubmitted(const QCA::SecureArray &password, const QCA::Event &event)
|
||||
{
|
||||
if (event.source() == QCA::Event::Data && !event.fileName().isEmpty())
|
||||
maybe[event.fileName()] = password;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// CertItem
|
||||
//----------------------------------------------------------------------------
|
||||
static QString escape(const QString &in)
|
||||
{
|
||||
QString out;
|
||||
for (int n = 0; n < in.length(); ++n) {
|
||||
if (in[n] == '\\')
|
||||
out += "\\\\";
|
||||
else if (in[n] == ':')
|
||||
out += "\\c";
|
||||
else if (in[n] == '\n')
|
||||
out += "\\n";
|
||||
else
|
||||
out += in[n];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static QString unescape(const QString &in)
|
||||
{
|
||||
QString out;
|
||||
for (int n = 0; n < in.length(); ++n) {
|
||||
if (in[n] == '\\') {
|
||||
if (n + 1 < in.length()) {
|
||||
++n;
|
||||
if (in[n] == '\\')
|
||||
out += '\\';
|
||||
else if (in[n] == 'c')
|
||||
out += ':';
|
||||
else if (in[n] == 'n')
|
||||
out += '\n';
|
||||
}
|
||||
} else
|
||||
out += in[n];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
class CertItem::Private : public QSharedData
|
||||
{
|
||||
public:
|
||||
QString name;
|
||||
QCA::CertificateChain chain;
|
||||
bool havePrivate;
|
||||
StorageType storageType;
|
||||
bool usable;
|
||||
|
||||
QString fileName;
|
||||
QCA::KeyStoreEntry keyStoreEntry;
|
||||
QString keyStoreEntryString;
|
||||
|
||||
Private()
|
||||
: havePrivate(false)
|
||||
, storageType(File)
|
||||
, usable(false)
|
||||
{
|
||||
}
|
||||
|
||||
QString toString() const
|
||||
{
|
||||
QStringList parts;
|
||||
|
||||
parts += name;
|
||||
parts += QString::number(chain.count());
|
||||
foreach (const QCA::Certificate &cert, chain)
|
||||
parts += QCA::Base64().arrayToString(cert.toDER());
|
||||
|
||||
if (havePrivate) {
|
||||
if (storageType == File) {
|
||||
parts += "privateFile";
|
||||
parts += fileName;
|
||||
} else // KeyStoreEntry
|
||||
{
|
||||
parts += "privateEntry";
|
||||
if (!keyStoreEntry.isNull())
|
||||
parts += keyStoreEntry.toString();
|
||||
else
|
||||
parts += keyStoreEntryString;
|
||||
}
|
||||
}
|
||||
|
||||
for (int n = 0; n < parts.count(); ++n)
|
||||
parts[n] = escape(parts[n]);
|
||||
return parts.join(":");
|
||||
}
|
||||
|
||||
bool fromString(const QString &in)
|
||||
{
|
||||
const QStringList parts = in.split(':');
|
||||
for (int n = 0; n < parts.count(); ++n)
|
||||
parts[n] = unescape(parts[n]);
|
||||
|
||||
if (parts.count() < 3)
|
||||
return false;
|
||||
|
||||
name = parts[0];
|
||||
int chainCount = parts[1].toInt();
|
||||
if (chainCount < 1 || chainCount > parts.count() - 2)
|
||||
return false;
|
||||
chain.clear();
|
||||
for (int n = 0; n < chainCount; ++n) {
|
||||
QCA::Certificate cert = QCA::Certificate::fromDER(QCA::Base64().stringToArray(parts[n + 2]).toByteArray());
|
||||
if (cert.isNull())
|
||||
return false;
|
||||
chain += cert;
|
||||
}
|
||||
int at = chain.count() + 2;
|
||||
|
||||
if (at < parts.count()) {
|
||||
havePrivate = true;
|
||||
usable = false;
|
||||
|
||||
if (parts[at] == "privateFile") {
|
||||
storageType = File;
|
||||
fileName = parts[at + 1];
|
||||
if (QFile::exists(fileName))
|
||||
usable = true;
|
||||
} else if (parts[at] == "privateEntry") {
|
||||
storageType = KeyStore;
|
||||
keyStoreEntryString = parts[at + 1];
|
||||
keyStoreEntry = QCA::KeyStoreEntry(keyStoreEntryString);
|
||||
if (!keyStoreEntry.isNull())
|
||||
usable = true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
CertItem::CertItem()
|
||||
{
|
||||
}
|
||||
|
||||
CertItem::CertItem(const CertItem &from)
|
||||
: d(from.d)
|
||||
{
|
||||
}
|
||||
|
||||
CertItem::~CertItem()
|
||||
{
|
||||
}
|
||||
|
||||
CertItem &CertItem::operator=(const CertItem &from)
|
||||
{
|
||||
d = from.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QString CertItem::name() const
|
||||
{
|
||||
return d->name;
|
||||
}
|
||||
|
||||
QCA::CertificateChain CertItem::certificateChain() const
|
||||
{
|
||||
return d->chain;
|
||||
}
|
||||
|
||||
bool CertItem::havePrivate() const
|
||||
{
|
||||
return d->havePrivate;
|
||||
}
|
||||
|
||||
CertItem::StorageType CertItem::storageType() const
|
||||
{
|
||||
return d->storageType;
|
||||
}
|
||||
|
||||
bool CertItem::isUsable() const
|
||||
{
|
||||
return d->usable;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// CertItemStore
|
||||
//----------------------------------------------------------------------------
|
||||
static MyPrompter *g_prompter = 0;
|
||||
static int g_prompter_refs = 0;
|
||||
|
||||
class CertItemStorePrivate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CertItemStore *q;
|
||||
MyPrompter *prompter;
|
||||
QList<CertItem> list;
|
||||
QList<int> idList;
|
||||
CertItemIconset iconset;
|
||||
int next_id;
|
||||
int next_req_id;
|
||||
|
||||
class LoaderItem
|
||||
{
|
||||
public:
|
||||
int req_id;
|
||||
QCA::KeyLoader *keyLoader;
|
||||
QString fileName;
|
||||
};
|
||||
|
||||
QList<LoaderItem> loaders;
|
||||
|
||||
CertItemStorePrivate(CertItemStore *_q)
|
||||
: QObject(_q)
|
||||
, q(_q)
|
||||
, next_id(0)
|
||||
, next_req_id(0)
|
||||
{
|
||||
if (!g_prompter) {
|
||||
g_prompter = new MyPrompter;
|
||||
g_prompter_refs = 1;
|
||||
} else
|
||||
++g_prompter_refs;
|
||||
|
||||
prompter = g_prompter;
|
||||
}
|
||||
|
||||
~CertItemStorePrivate()
|
||||
{
|
||||
foreach (const LoaderItem &i, loaders)
|
||||
delete i.keyLoader;
|
||||
|
||||
--g_prompter_refs;
|
||||
if (g_prompter_refs == 0) {
|
||||
delete g_prompter;
|
||||
g_prompter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
QString getUniqueName(const QString &name)
|
||||
{
|
||||
int num = 1;
|
||||
while (1) {
|
||||
QString tryname;
|
||||
if (num == 1)
|
||||
tryname = name;
|
||||
else
|
||||
tryname = name + QString(" (%1)").arg(num);
|
||||
|
||||
bool found = false;
|
||||
foreach (const CertItem &i, list) {
|
||||
if (i.name() == tryname) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return tryname;
|
||||
|
||||
++num;
|
||||
}
|
||||
}
|
||||
|
||||
static QString convertErrorToString(QCA::ConvertResult r)
|
||||
{
|
||||
QString str;
|
||||
switch (r) {
|
||||
case QCA::ConvertGood:
|
||||
break;
|
||||
case QCA::ErrorPassphrase:
|
||||
str = tr("Incorrect passphrase.");
|
||||
case QCA::ErrorFile:
|
||||
str = tr("Unable to open or read file.");
|
||||
case QCA::ErrorDecode:
|
||||
default:
|
||||
str = tr("Unable to decode format.");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
void loader_finished()
|
||||
{
|
||||
QCA::KeyLoader *keyLoader = (QCA::KeyLoader *)sender();
|
||||
int at = -1;
|
||||
for (int n = 0; n < loaders.count(); ++n) {
|
||||
if (loaders[n].keyLoader == keyLoader) {
|
||||
at = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Q_ASSERT(at != -1);
|
||||
|
||||
int req_id = loaders[at].req_id;
|
||||
QString fileName = loaders[at].fileName;
|
||||
loaders.removeAt(at);
|
||||
|
||||
QCA::ConvertResult r = keyLoader->convertResult();
|
||||
if (r != QCA::ConvertGood) {
|
||||
delete keyLoader;
|
||||
prompter->fileFailed(fileName);
|
||||
QMessageBox::information(
|
||||
0,
|
||||
tr("Error"),
|
||||
tr("Error importing certificate and private key.\nReason: %1").arg(convertErrorToString(r)));
|
||||
emit q->addFailed(req_id);
|
||||
return;
|
||||
}
|
||||
|
||||
prompter->fileSuccess(fileName);
|
||||
|
||||
QCA::KeyBundle kb = keyLoader->keyBundle();
|
||||
delete keyLoader;
|
||||
|
||||
QCA::CertificateChain chain = kb.certificateChain();
|
||||
QCA::Certificate cert = chain.primary();
|
||||
|
||||
QString name = getUniqueName(cert.commonName());
|
||||
|
||||
CertItem i;
|
||||
i.d = new CertItem::Private;
|
||||
i.d->name = name;
|
||||
i.d->chain = chain;
|
||||
i.d->havePrivate = true;
|
||||
i.d->storageType = CertItem::File;
|
||||
i.d->usable = true;
|
||||
i.d->fileName = fileName;
|
||||
|
||||
int id = next_id++;
|
||||
|
||||
q->beginInsertRows(QModelIndex(), list.size(), list.size());
|
||||
list += i;
|
||||
idList += id;
|
||||
q->endInsertRows();
|
||||
|
||||
emit q->addSuccess(req_id, id);
|
||||
}
|
||||
};
|
||||
|
||||
CertItemStore::CertItemStore(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
d = new CertItemStorePrivate(this);
|
||||
}
|
||||
|
||||
CertItemStore::~CertItemStore()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int CertItemStore::idFromRow(int row) const
|
||||
{
|
||||
return d->idList[row];
|
||||
}
|
||||
|
||||
int CertItemStore::rowFromId(int id) const
|
||||
{
|
||||
for (int n = 0; n < d->idList.count(); ++n) {
|
||||
if (d->idList[n] == id)
|
||||
return n;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
CertItem CertItemStore::itemFromId(int id) const
|
||||
{
|
||||
return d->list[rowFromId(id)];
|
||||
}
|
||||
|
||||
CertItem CertItemStore::itemFromRow(int row) const
|
||||
{
|
||||
return d->list[row];
|
||||
}
|
||||
|
||||
QList<CertItem> CertItemStore::items() const
|
||||
{
|
||||
return d->list;
|
||||
}
|
||||
|
||||
QStringList CertItemStore::save() const
|
||||
{
|
||||
QStringList out;
|
||||
foreach (const CertItem &i, d->list)
|
||||
out += i.d->toString();
|
||||
return out;
|
||||
}
|
||||
|
||||
bool CertItemStore::load(const QStringList &in)
|
||||
{
|
||||
QList<CertItem> addList;
|
||||
QList<int> addIdList;
|
||||
foreach (const QString &s, in) {
|
||||
CertItem i;
|
||||
i.d = new CertItem::Private;
|
||||
if (i.d->fromString(s)) {
|
||||
addList += i;
|
||||
addIdList += d->next_id++;
|
||||
}
|
||||
}
|
||||
|
||||
if (addList.isEmpty())
|
||||
return true;
|
||||
|
||||
beginInsertRows(QModelIndex(), d->list.size(), d->list.size() + addList.count() - 1);
|
||||
d->list += addList;
|
||||
d->idList += addIdList;
|
||||
endInsertRows();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CertItemStore::addFromFile(const QString &fileName)
|
||||
{
|
||||
CertItemStorePrivate::LoaderItem i;
|
||||
i.req_id = d->next_req_id++;
|
||||
i.keyLoader = new QCA::KeyLoader(d);
|
||||
i.fileName = fileName;
|
||||
connect(i.keyLoader, SIGNAL(finished()), d, SLOT(loader_finished()));
|
||||
d->loaders += i;
|
||||
i.keyLoader->loadKeyBundleFromFile(fileName);
|
||||
return i.req_id;
|
||||
}
|
||||
|
||||
int CertItemStore::addFromKeyStore(const QCA::KeyStoreEntry &entry)
|
||||
{
|
||||
QCA::KeyBundle kb = entry.keyBundle();
|
||||
|
||||
QCA::CertificateChain chain = kb.certificateChain();
|
||||
QCA::Certificate cert = chain.primary();
|
||||
|
||||
QString name = d->getUniqueName(entry.name());
|
||||
|
||||
CertItem i;
|
||||
i.d = new CertItem::Private;
|
||||
i.d->name = name;
|
||||
i.d->chain = chain;
|
||||
i.d->havePrivate = true;
|
||||
i.d->storageType = CertItem::KeyStore;
|
||||
i.d->usable = true;
|
||||
i.d->keyStoreEntry = entry;
|
||||
|
||||
int id = d->next_id++;
|
||||
|
||||
beginInsertRows(QModelIndex(), d->list.size(), d->list.size());
|
||||
d->list += i;
|
||||
d->idList += id;
|
||||
endInsertRows();
|
||||
|
||||
int req_id = d->next_req_id++;
|
||||
QMetaObject::invokeMethod(this, "addSuccess", Qt::QueuedConnection, Q_ARG(int, req_id), Q_ARG(int, id));
|
||||
return req_id;
|
||||
}
|
||||
|
||||
int CertItemStore::addUser(const QCA::CertificateChain &chain)
|
||||
{
|
||||
QCA::Certificate cert = chain.primary();
|
||||
|
||||
QString name = d->getUniqueName(cert.commonName());
|
||||
|
||||
CertItem i;
|
||||
i.d = new CertItem::Private;
|
||||
i.d->name = name;
|
||||
i.d->chain = chain;
|
||||
|
||||
int id = d->next_id++;
|
||||
|
||||
beginInsertRows(QModelIndex(), d->list.size(), d->list.size());
|
||||
d->list += i;
|
||||
d->idList += id;
|
||||
endInsertRows();
|
||||
|
||||
int req_id = d->next_req_id++;
|
||||
QMetaObject::invokeMethod(this, "addSuccess", Qt::QueuedConnection, Q_ARG(int, req_id), Q_ARG(int, id));
|
||||
return req_id;
|
||||
}
|
||||
|
||||
void CertItemStore::updateChain(int id, const QCA::CertificateChain &chain)
|
||||
{
|
||||
int at = rowFromId(id);
|
||||
d->list[at].d->chain = chain;
|
||||
}
|
||||
|
||||
void CertItemStore::removeItem(int id)
|
||||
{
|
||||
int at = rowFromId(id);
|
||||
|
||||
beginRemoveRows(QModelIndex(), at, at);
|
||||
d->list.removeAt(at);
|
||||
d->idList.removeAt(at);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
void CertItemStore::setIcon(IconType type, const QPixmap &icon)
|
||||
{
|
||||
d->iconset[type] = icon;
|
||||
}
|
||||
|
||||
int CertItemStore::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return d->list.count();
|
||||
}
|
||||
|
||||
QVariant CertItemStore::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
int at = index.row();
|
||||
QList<CertItem> &list = d->list;
|
||||
|
||||
if (at >= list.count())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
QString str = list[at].name();
|
||||
if (list[at].havePrivate() && !list[at].isUsable())
|
||||
str += QString(" ") + tr("(not usable)");
|
||||
return str;
|
||||
} else if (role == Qt::EditRole)
|
||||
return list[at].name();
|
||||
else if (role == Qt::DecorationRole) {
|
||||
if (list[at].havePrivate())
|
||||
return d->iconset[CertItemStore::IconKeyBundle];
|
||||
else
|
||||
return d->iconset[CertItemStore::IconCert];
|
||||
} else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags CertItemStore::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return Qt::ItemIsEnabled;
|
||||
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
bool CertItemStore::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (index.isValid() && role == Qt::EditRole) {
|
||||
QString str = value.toString();
|
||||
d->list[index.row()].d->name = str;
|
||||
emit dataChanged(index, index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// CertItemPrivateLoader
|
||||
//----------------------------------------------------------------------------
|
||||
class CertItemPrivateLoaderPrivate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CertItemPrivateLoader *q;
|
||||
CertItemStore *store;
|
||||
QCA::KeyLoader *loader;
|
||||
QString fileName;
|
||||
QCA::PrivateKey key;
|
||||
|
||||
CertItemPrivateLoaderPrivate(CertItemPrivateLoader *_q)
|
||||
: QObject(_q)
|
||||
, q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
void loader_finished()
|
||||
{
|
||||
QCA::ConvertResult r = loader->convertResult();
|
||||
if (r != QCA::ConvertGood) {
|
||||
delete loader;
|
||||
loader = 0;
|
||||
store->d->prompter->fileFailed(fileName);
|
||||
QMessageBox::information(
|
||||
0,
|
||||
tr("Error"),
|
||||
tr("Error accessing private key.\nReason: %1").arg(CertItemStorePrivate::convertErrorToString(r)));
|
||||
emit q->finished();
|
||||
return;
|
||||
}
|
||||
|
||||
store->d->prompter->fileSuccess(fileName);
|
||||
|
||||
key = loader->keyBundle().privateKey();
|
||||
delete loader;
|
||||
loader = 0;
|
||||
emit q->finished();
|
||||
}
|
||||
};
|
||||
|
||||
CertItemPrivateLoader::CertItemPrivateLoader(CertItemStore *store, QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
d = new CertItemPrivateLoaderPrivate(this);
|
||||
d->store = store;
|
||||
}
|
||||
|
||||
CertItemPrivateLoader::~CertItemPrivateLoader()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void CertItemPrivateLoader::start(int id)
|
||||
{
|
||||
CertItem i = d->store->itemFromId(id);
|
||||
|
||||
if (i.storageType() == CertItem::KeyStore) {
|
||||
d->key = i.d->keyStoreEntry.keyBundle().privateKey();
|
||||
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
d->key = QCA::PrivateKey();
|
||||
d->fileName = i.d->fileName;
|
||||
d->loader = new QCA::KeyLoader(d);
|
||||
connect(d->loader, SIGNAL(finished()), d, SLOT(loader_finished()));
|
||||
d->loader->loadKeyBundleFromFile(d->fileName);
|
||||
}
|
||||
|
||||
QCA::PrivateKey CertItemPrivateLoader::privateKey() const
|
||||
{
|
||||
return d->key;
|
||||
}
|
||||
|
||||
#include "certitem.moc"
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CERTITEM_H
|
||||
#define CERTITEM_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QSharedDataPointer>
|
||||
|
||||
class QString;
|
||||
class QStringList;
|
||||
|
||||
namespace QCA {
|
||||
class PrivateKey;
|
||||
class CertificateChain;
|
||||
class KeyStoreEntry;
|
||||
}
|
||||
|
||||
class CertItemStore;
|
||||
class CertItemStorePrivate;
|
||||
class CertItemPrivateLoaderPrivate;
|
||||
|
||||
class CertItem
|
||||
{
|
||||
public:
|
||||
enum StorageType
|
||||
{
|
||||
File,
|
||||
KeyStore
|
||||
};
|
||||
|
||||
CertItem();
|
||||
CertItem(const CertItem &from);
|
||||
~CertItem();
|
||||
CertItem &operator=(const CertItem &from);
|
||||
|
||||
QString name() const;
|
||||
QCA::CertificateChain certificateChain() const;
|
||||
bool havePrivate() const;
|
||||
StorageType storageType() const; // private key storage type
|
||||
bool isUsable() const; // file/provider present
|
||||
|
||||
private:
|
||||
class Private;
|
||||
QSharedDataPointer<Private> d;
|
||||
|
||||
friend class CertItemStore;
|
||||
friend class CertItemStorePrivate;
|
||||
friend class CertItemPrivateLoader;
|
||||
friend class CertItemPrivateLoaderPrivate;
|
||||
};
|
||||
|
||||
class CertItemStore : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum IconType
|
||||
{
|
||||
IconCert,
|
||||
IconCrl,
|
||||
IconKeyBundle,
|
||||
IconPgpPub,
|
||||
IconPgpSec
|
||||
};
|
||||
|
||||
CertItemStore(QObject *parent = nullptr);
|
||||
~CertItemStore();
|
||||
|
||||
int idFromRow(int row) const;
|
||||
int rowFromId(int id) const;
|
||||
CertItem itemFromId(int id) const;
|
||||
CertItem itemFromRow(int row) const;
|
||||
|
||||
QList<CertItem> items() const;
|
||||
|
||||
QStringList save() const;
|
||||
bool load(const QStringList &in);
|
||||
|
||||
// returns a reqId
|
||||
int addFromFile(const QString &fileName);
|
||||
int addFromKeyStore(const QCA::KeyStoreEntry &entry);
|
||||
int addUser(const QCA::CertificateChain &chain);
|
||||
|
||||
void updateChain(int id, const QCA::CertificateChain &chain);
|
||||
|
||||
void removeItem(int id);
|
||||
|
||||
void setIcon(IconType type, const QPixmap &icon);
|
||||
|
||||
// reimplemented
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
|
||||
Q_SIGNALS:
|
||||
void addSuccess(int reqId, int id);
|
||||
void addFailed(int reqId);
|
||||
|
||||
private:
|
||||
friend class CertItemStorePrivate;
|
||||
CertItemStorePrivate *d;
|
||||
|
||||
friend class CertItemPrivateLoader;
|
||||
friend class CertItemPrivateLoaderPrivate;
|
||||
};
|
||||
|
||||
class CertItemPrivateLoader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CertItemPrivateLoader(CertItemStore *store, QObject *parent = nullptr);
|
||||
~CertItemPrivateLoader();
|
||||
|
||||
void start(int id);
|
||||
|
||||
QCA::PrivateKey privateKey() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void finished();
|
||||
|
||||
private:
|
||||
friend class CertItemPrivateLoaderPrivate;
|
||||
CertItemPrivateLoaderPrivate *d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,85 @@
|
||||
<ui version="4.0" >
|
||||
<class>CertView</class>
|
||||
<widget class="QDialog" name="CertView" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>View Certificate</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cb_chain" />
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame" >
|
||||
<property name="frameShape" >
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="lb_info" >
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons" >
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>cb_chain</tabstop>
|
||||
<tabstop>buttonBox</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>CertView</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>284</x>
|
||||
<y>278</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>291</x>
|
||||
<y>299</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "certviewdlg.h"
|
||||
|
||||
#include "ui_certview.h"
|
||||
#include <QtCore>
|
||||
#include <QtCrypto>
|
||||
#include <QtGui>
|
||||
|
||||
// from qcatool
|
||||
class InfoType
|
||||
{
|
||||
public:
|
||||
QCA::CertificateInfoType type;
|
||||
QString varname;
|
||||
QString shortname;
|
||||
QString name;
|
||||
QString desc;
|
||||
|
||||
InfoType()
|
||||
{
|
||||
}
|
||||
|
||||
InfoType(QCA::CertificateInfoType _type,
|
||||
const QString &_varname,
|
||||
const QString &_shortname,
|
||||
const QString &_name,
|
||||
const QString &_desc)
|
||||
: type(_type)
|
||||
, varname(_varname)
|
||||
, shortname(_shortname)
|
||||
, name(_name)
|
||||
, desc(_desc)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static QList<InfoType> makeInfoTypeList(bool legacyEmail = false)
|
||||
{
|
||||
QList<InfoType> out;
|
||||
out += InfoType(
|
||||
QCA::CommonName, "CommonName", "CN", CertViewDlg::tr("Common Name (CN)"), "Full name, domain, anything");
|
||||
out += InfoType(QCA::Email, "Email", "", CertViewDlg::tr("Email Address"), "");
|
||||
if (legacyEmail)
|
||||
out += InfoType(QCA::EmailLegacy, "EmailLegacy", "", CertViewDlg::tr("PKCS#9 Email Address"), "");
|
||||
out += InfoType(QCA::Organization, "Organization", "O", CertViewDlg::tr("Organization (O)"), "Company, group, etc");
|
||||
out += InfoType(QCA::OrganizationalUnit,
|
||||
"OrganizationalUnit",
|
||||
"OU",
|
||||
CertViewDlg::tr("Organizational Unit (OU)"),
|
||||
"Division/branch of organization");
|
||||
out += InfoType(QCA::Locality, "Locality", "", CertViewDlg::tr("Locality (L)"), "City, shire, part of a state");
|
||||
out += InfoType(QCA::State, "State", "", CertViewDlg::tr("State (ST)"), "State within the country");
|
||||
out += InfoType(QCA::Country, "Country", "C", CertViewDlg::tr("Country Code (C)"), "2-letter code");
|
||||
out += InfoType(QCA::IncorporationLocality,
|
||||
"IncorporationLocality",
|
||||
"",
|
||||
CertViewDlg::tr("Incorporation Locality"),
|
||||
"For EV certificates");
|
||||
out += InfoType(QCA::IncorporationState,
|
||||
"IncorporationState",
|
||||
"",
|
||||
CertViewDlg::tr("Incorporation State"),
|
||||
"For EV certificates");
|
||||
out += InfoType(QCA::IncorporationCountry,
|
||||
"IncorporationCountry",
|
||||
"",
|
||||
CertViewDlg::tr("Incorporation Country"),
|
||||
"For EV certificates");
|
||||
out += InfoType(QCA::URI, "URI", "", CertViewDlg::tr("URI"), "");
|
||||
out += InfoType(QCA::DNS, "DNS", "", CertViewDlg::tr("Domain Name"), "Domain (dnsName)");
|
||||
out += InfoType(QCA::IPAddress, "IPAddress", "", CertViewDlg::tr("IP Adddress"), "");
|
||||
out += InfoType(QCA::XMPP, "XMPP", "", CertViewDlg::tr("XMPP Address (JID)"), "From RFC 3920 (id-on-xmppAddr)");
|
||||
return out;
|
||||
}
|
||||
|
||||
static QString try_print_info(const QString &name, const QStringList &values)
|
||||
{
|
||||
QString out;
|
||||
if (!values.isEmpty()) {
|
||||
QString value = values.join(", ");
|
||||
out = QString(" ") + CertViewDlg::tr("%1: %2").arg(name, value) + '\n';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static QString print_info(const QString &title, const QCA::CertificateInfo &info)
|
||||
{
|
||||
QString out;
|
||||
QList<InfoType> list = makeInfoTypeList();
|
||||
out += title + '\n';
|
||||
foreach (const InfoType &t, list)
|
||||
out += try_print_info(t.name, info.values(t.type));
|
||||
return out;
|
||||
}
|
||||
|
||||
static QString cert_info_string(const QCA::Certificate &cert)
|
||||
{
|
||||
QString out;
|
||||
out += CertViewDlg::tr("Serial Number: %1").arg(cert.serialNumber().toString()) + '\n';
|
||||
out += print_info(CertViewDlg::tr("Subject"), cert.subjectInfo());
|
||||
out += print_info(CertViewDlg::tr("Issuer"), cert.issuerInfo());
|
||||
out += CertViewDlg::tr("Validity") + '\n';
|
||||
out += QString(" ") + CertViewDlg::tr("Not before: %1").arg(cert.notValidBefore().toString()) + '\n';
|
||||
out += QString(" ") + CertViewDlg::tr("Not after: %1").arg(cert.notValidAfter().toString()) + '\n';
|
||||
return out;
|
||||
}
|
||||
|
||||
class CertViewDlg::Private : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CertViewDlg *q;
|
||||
Ui_CertView ui;
|
||||
QCA::CertificateChain chain;
|
||||
|
||||
Private(CertViewDlg *_q)
|
||||
: QObject(_q)
|
||||
, q(_q)
|
||||
{
|
||||
ui.setupUi(q);
|
||||
connect(ui.cb_chain, SIGNAL(activated(int)), SLOT(cb_activated(int)));
|
||||
ui.lb_info->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
QStringList names = QCA::makeFriendlyNames(chain);
|
||||
ui.cb_chain->clear();
|
||||
foreach (const QString &s, names)
|
||||
ui.cb_chain->insertItem(ui.cb_chain->count(), s);
|
||||
updateInfo();
|
||||
}
|
||||
|
||||
void updateInfo()
|
||||
{
|
||||
int x = ui.cb_chain->currentIndex();
|
||||
if (x == -1) {
|
||||
ui.lb_info->setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
ui.lb_info->setText(cert_info_string(chain[x]));
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void cb_activated(int)
|
||||
{
|
||||
updateInfo();
|
||||
}
|
||||
};
|
||||
|
||||
CertViewDlg::CertViewDlg(const QCA::CertificateChain &chain, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
d = new Private(this);
|
||||
d->chain = chain;
|
||||
d->update();
|
||||
}
|
||||
|
||||
CertViewDlg::~CertViewDlg()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
#include "certviewdlg.moc"
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CERTVIEWDLG_H
|
||||
#define CERTVIEWDLG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace QCA {
|
||||
class CertificateChain;
|
||||
}
|
||||
|
||||
class CertViewDlg : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CertViewDlg(const QCA::CertificateChain &chain, QWidget *parent = 0);
|
||||
~CertViewDlg();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
<RCC>
|
||||
<qresource prefix="/gfx" >
|
||||
<file>icons/cert16.png</file>
|
||||
<file>icons/crl16.png</file>
|
||||
<file>icons/keybundle16.png</file>
|
||||
<file>icons/keypair16.png</file>
|
||||
<file>icons/publickey16.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
After Width: | Height: | Size: 638 B |
|
After Width: | Height: | Size: 693 B |
|
After Width: | Height: | Size: 786 B |
|
After Width: | Height: | Size: 835 B |
|
After Width: | Height: | Size: 617 B |
@@ -0,0 +1,96 @@
|
||||
<ui version="4.0" >
|
||||
<class>KeySelect</class>
|
||||
<widget class="QDialog" name="KeySelect" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>363</width>
|
||||
<height>233</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Storage Device Browser</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<string>Select an identity from a storage device below:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="lv_stores" />
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="lb_busy" />
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons" >
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>lv_stores</tabstop>
|
||||
<tabstop>buttonBox</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>KeySelect</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>227</x>
|
||||
<y>211</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>157</x>
|
||||
<y>232</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>KeySelect</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>295</x>
|
||||
<y>217</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>286</x>
|
||||
<y>232</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "keyselectdlg.h"
|
||||
|
||||
#include "ui_keyselect.h"
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QtCore>
|
||||
#include <QtCrypto>
|
||||
#include <QtGui>
|
||||
|
||||
#define ONLY_SHOW_KEYBUNDLE
|
||||
|
||||
typedef QMap<KeySelectDlg::IconType, QPixmap> KeyStoreIconset;
|
||||
|
||||
class KeyStoreItemShared
|
||||
{
|
||||
public:
|
||||
KeyStoreIconset iconset;
|
||||
QString notAvailableString;
|
||||
};
|
||||
|
||||
class KeyStoreItem : public QStandardItem
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Store = UserType,
|
||||
Entry
|
||||
};
|
||||
|
||||
enum Role
|
||||
{
|
||||
NameRole = Qt::UserRole,
|
||||
SubTypeRole,
|
||||
AvailabilityRole,
|
||||
PositionRole
|
||||
};
|
||||
|
||||
QPixmap entryTypeToIcon(QCA::KeyStoreEntry::Type type) const
|
||||
{
|
||||
QPixmap out;
|
||||
if (!_shared)
|
||||
return out;
|
||||
const KeyStoreIconset &iconset = _shared->iconset;
|
||||
switch (type) {
|
||||
case QCA::KeyStoreEntry::TypeKeyBundle:
|
||||
out = iconset[KeySelectDlg::IconKeyBundle];
|
||||
break;
|
||||
case QCA::KeyStoreEntry::TypeCertificate:
|
||||
out = iconset[KeySelectDlg::IconCert];
|
||||
break;
|
||||
case QCA::KeyStoreEntry::TypeCRL:
|
||||
out = iconset[KeySelectDlg::IconCrl];
|
||||
break;
|
||||
case QCA::KeyStoreEntry::TypePGPSecretKey:
|
||||
out = iconset[KeySelectDlg::IconPgpSec];
|
||||
break;
|
||||
case QCA::KeyStoreEntry::TypePGPPublicKey:
|
||||
out = iconset[KeySelectDlg::IconPgpPub];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Type _type;
|
||||
KeyStoreItemShared *_shared;
|
||||
|
||||
QCA::KeyStore *keyStore;
|
||||
QCA::KeyStoreEntry keyStoreEntry;
|
||||
|
||||
KeyStoreItem(Type type, KeyStoreItemShared *shared)
|
||||
: _type(type)
|
||||
, _shared(shared)
|
||||
{
|
||||
setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
||||
}
|
||||
|
||||
void setStore(const QString &name, QCA::KeyStore::Type type)
|
||||
{
|
||||
setData(name, NameRole);
|
||||
setData((int)type, SubTypeRole);
|
||||
}
|
||||
|
||||
void setEntry(const QString &name, QCA::KeyStoreEntry::Type type, bool available, int pos)
|
||||
{
|
||||
setData(name, NameRole);
|
||||
setData((int)type, SubTypeRole);
|
||||
setData(available, AvailabilityRole);
|
||||
setData(pos, PositionRole);
|
||||
}
|
||||
|
||||
virtual QVariant data(int role) const
|
||||
{
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (_type == Store) {
|
||||
return data(NameRole).toString();
|
||||
} else if (_type == Entry) {
|
||||
QString str = data(NameRole).toString();
|
||||
if (_shared && !data(AvailabilityRole).toBool())
|
||||
str += QString(" ") + _shared->notAvailableString;
|
||||
return str;
|
||||
} else
|
||||
return QStandardItem::data(role);
|
||||
} else if (role == Qt::DecorationRole) {
|
||||
if (_type == Entry) {
|
||||
QCA::KeyStoreEntry::Type type = (QCA::KeyStoreEntry::Type)data(SubTypeRole).toInt();
|
||||
return entryTypeToIcon(type);
|
||||
} else
|
||||
return QStandardItem::data(role);
|
||||
} else
|
||||
return QStandardItem::data(role);
|
||||
}
|
||||
|
||||
virtual int type() const
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
virtual QStandardItem *clone() const
|
||||
{
|
||||
return new KeyStoreItem(*this);
|
||||
}
|
||||
};
|
||||
|
||||
class KeyStoreModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyStoreItemShared shared;
|
||||
|
||||
QCA::KeyStoreManager ksm;
|
||||
|
||||
KeyStoreModel(QObject *parent = 0)
|
||||
: QStandardItemModel(parent)
|
||||
, ksm(this)
|
||||
{
|
||||
shared.notAvailableString = tr("(not available)");
|
||||
|
||||
// make sure keystores are started
|
||||
QCA::KeyStoreManager::start();
|
||||
|
||||
connect(&ksm, SIGNAL(keyStoreAvailable(const QString &)), SLOT(ks_available(const QString &)));
|
||||
QStringList list = ksm.keyStores();
|
||||
foreach (const QString &s, list)
|
||||
ks_available(s);
|
||||
|
||||
setSortRole(KeyStoreItem::PositionRole);
|
||||
}
|
||||
|
||||
KeyStoreItem *itemFromStore(QCA::KeyStore *ks) const
|
||||
{
|
||||
for (int n = 0; n < rowCount(); ++n) {
|
||||
KeyStoreItem *i = (KeyStoreItem *)item(n);
|
||||
if (i->keyStore == ks)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void ks_available(const QString &keyStoreId)
|
||||
{
|
||||
QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, &ksm);
|
||||
|
||||
#ifdef ONLY_SHOW_KEYBUNDLE
|
||||
// only list stores containing keybundles (non-pgp identities)
|
||||
if (!ks->holdsIdentities() || ks->type() == QCA::KeyStore::PGPKeyring)
|
||||
return;
|
||||
#endif
|
||||
|
||||
connect(ks, SIGNAL(updated()), SLOT(ks_updated()));
|
||||
connect(ks, SIGNAL(unavailable()), SLOT(ks_unavailable()));
|
||||
|
||||
KeyStoreItem *store_item = new KeyStoreItem(KeyStoreItem::Store, &shared);
|
||||
store_item->setStore(ks->name(), ks->type());
|
||||
store_item->keyStore = ks;
|
||||
ks->startAsynchronousMode();
|
||||
appendRow(store_item);
|
||||
}
|
||||
|
||||
void ks_updated()
|
||||
{
|
||||
QCA::KeyStore *ks = (QCA::KeyStore *)sender();
|
||||
KeyStoreItem *store_item = itemFromStore(ks);
|
||||
Q_ASSERT(store_item);
|
||||
|
||||
QList<QCA::KeyStoreEntry> newEntries = ks->entryList();
|
||||
|
||||
#ifdef ONLY_SHOW_KEYBUNDLE
|
||||
// ignore entries that are not keybundles
|
||||
for (int n = 0; n < newEntries.count(); ++n) {
|
||||
if (newEntries[n].type() != QCA::KeyStoreEntry::TypeKeyBundle) {
|
||||
newEntries.removeAt(n);
|
||||
--n; // adjust position
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// update the store item itself
|
||||
store_item->setStore(ks->name(), ks->type());
|
||||
|
||||
// handle removed child entries
|
||||
for (int n = 0; n < store_item->rowCount(); ++n) {
|
||||
KeyStoreItem *i = (KeyStoreItem *)store_item->child(n);
|
||||
|
||||
// is the existing entry in the new list?
|
||||
bool found = false;
|
||||
foreach (const QCA::KeyStoreEntry &ne, newEntries) {
|
||||
if (ne.id() == i->keyStoreEntry.id()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if not, remove it
|
||||
if (!found) {
|
||||
store_item->removeRow(n);
|
||||
--n; // adjust position
|
||||
}
|
||||
}
|
||||
|
||||
// handle added/updated child entries
|
||||
for (int n = 0; n < newEntries.count(); ++n) {
|
||||
const QCA::KeyStoreEntry &ne = newEntries[n];
|
||||
|
||||
// was this entry in the original list?
|
||||
KeyStoreItem *entry_item = 0;
|
||||
for (int k = 0; k < store_item->rowCount(); ++k) {
|
||||
KeyStoreItem *i = (KeyStoreItem *)store_item->child(k);
|
||||
if (i->keyStoreEntry.id() == ne.id()) {
|
||||
entry_item = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if not, add it
|
||||
if (!entry_item) {
|
||||
entry_item = new KeyStoreItem(KeyStoreItem::Entry, &shared);
|
||||
entry_item->keyStoreEntry = ne;
|
||||
entry_item->setEntry(newEntries[n].name(), newEntries[n].type(), newEntries[n].isAvailable(), n);
|
||||
store_item->appendRow(entry_item);
|
||||
}
|
||||
// if so, update it
|
||||
else {
|
||||
entry_item->keyStoreEntry = ne;
|
||||
entry_item->setEntry(newEntries[n].name(), newEntries[n].type(), newEntries[n].isAvailable(), n);
|
||||
}
|
||||
}
|
||||
|
||||
store_item->sortChildren(0);
|
||||
}
|
||||
|
||||
void ks_unavailable()
|
||||
{
|
||||
QCA::KeyStore *ks = (QCA::KeyStore *)sender();
|
||||
KeyStoreItem *store_item = itemFromStore(ks);
|
||||
Q_ASSERT(store_item);
|
||||
|
||||
store_item->removeRows(0, store_item->rowCount());
|
||||
removeRow(store_item->row());
|
||||
delete ks;
|
||||
}
|
||||
};
|
||||
|
||||
class KeySelectDlg::Private : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeySelectDlg *q;
|
||||
Ui_KeySelect ui;
|
||||
KeyStoreModel *model;
|
||||
QCA::KeyStoreEntry cur_entry;
|
||||
QAction *actionView;
|
||||
|
||||
Private(KeySelectDlg *_q)
|
||||
: QObject(_q)
|
||||
, q(_q)
|
||||
{
|
||||
ui.setupUi(q);
|
||||
|
||||
model = new KeyStoreModel(this);
|
||||
connect(&model->ksm, SIGNAL(busyStarted()), SLOT(ksm_busyStarted()));
|
||||
connect(&model->ksm, SIGNAL(busyFinished()), SLOT(ksm_busyFinished()));
|
||||
if (model->ksm.isBusy())
|
||||
ksm_busyStarted();
|
||||
|
||||
ui.lv_stores->header()->hide();
|
||||
ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
ui.lv_stores->setModel(model);
|
||||
ui.lv_stores->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui.lv_stores->selectionModel(),
|
||||
SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
|
||||
SLOT(stores_selectionChanged(const QItemSelection &, const QItemSelection &)));
|
||||
connect(ui.lv_stores,
|
||||
SIGNAL(customContextMenuRequested(const QPoint &)),
|
||||
SLOT(stores_customContextMenuRequested(const QPoint &)));
|
||||
|
||||
actionView = new QAction(tr("&View"), this);
|
||||
connect(actionView, SIGNAL(triggered()), SLOT(view()));
|
||||
actionView->setEnabled(false);
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void ksm_busyStarted()
|
||||
{
|
||||
ui.lb_busy->setText(tr("Looking for devices..."));
|
||||
}
|
||||
|
||||
void ksm_busyFinished()
|
||||
{
|
||||
ui.lb_busy->setText("");
|
||||
}
|
||||
|
||||
void stores_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
||||
{
|
||||
Q_UNUSED(deselected);
|
||||
|
||||
KeyStoreItem *i = 0;
|
||||
if (!selected.indexes().isEmpty()) {
|
||||
QModelIndex index = selected.indexes().first();
|
||||
i = (KeyStoreItem *)model->itemFromIndex(index);
|
||||
}
|
||||
|
||||
bool viewable = false;
|
||||
bool choosable = false;
|
||||
if (i && i->type() == KeyStoreItem::Entry) {
|
||||
QCA::KeyStoreEntry entry = i->keyStoreEntry;
|
||||
if (entry.type() == QCA::KeyStoreEntry::TypeKeyBundle) {
|
||||
viewable = true;
|
||||
choosable = true;
|
||||
cur_entry = entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (!choosable)
|
||||
cur_entry = QCA::KeyStoreEntry();
|
||||
|
||||
actionView->setEnabled(viewable);
|
||||
|
||||
QPushButton *ok = ui.buttonBox->button(QDialogButtonBox::Ok);
|
||||
if (choosable && !ok->isEnabled())
|
||||
ok->setEnabled(true);
|
||||
else if (!choosable && ok->isEnabled())
|
||||
ok->setEnabled(false);
|
||||
}
|
||||
|
||||
void stores_customContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
QItemSelection selection = ui.lv_stores->selectionModel()->selection();
|
||||
if (selection.indexes().isEmpty())
|
||||
return;
|
||||
|
||||
QModelIndex index = selection.indexes().first();
|
||||
KeyStoreItem *i = (KeyStoreItem *)model->itemFromIndex(index);
|
||||
if (i && i->type() == KeyStoreItem::Entry) {
|
||||
QMenu menu(q);
|
||||
menu.addAction(actionView);
|
||||
menu.exec(ui.lv_stores->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
}
|
||||
|
||||
void view()
|
||||
{
|
||||
emit q->viewCertificate(cur_entry.keyBundle().certificateChain());
|
||||
}
|
||||
};
|
||||
|
||||
KeySelectDlg::KeySelectDlg(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
d = new Private(this);
|
||||
}
|
||||
|
||||
KeySelectDlg::~KeySelectDlg()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void KeySelectDlg::setIcon(IconType type, const QPixmap &icon)
|
||||
{
|
||||
d->model->shared.iconset[type] = icon;
|
||||
}
|
||||
|
||||
void KeySelectDlg::accept()
|
||||
{
|
||||
QCA::KeyStoreEntry entry = d->cur_entry;
|
||||
QDialog::accept();
|
||||
emit selected(entry);
|
||||
}
|
||||
|
||||
#include "keyselectdlg.moc"
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef KEYSELECTDLG_H
|
||||
#define KEYSELECTDLG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QPixmap;
|
||||
|
||||
namespace QCA {
|
||||
class CertificateChain;
|
||||
class KeyStoreEntry;
|
||||
}
|
||||
|
||||
class KeySelectDlg : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum IconType
|
||||
{
|
||||
IconCert,
|
||||
IconCrl,
|
||||
IconKeyBundle,
|
||||
IconPgpPub,
|
||||
IconPgpSec
|
||||
};
|
||||
|
||||
KeySelectDlg(QWidget *parent = 0);
|
||||
~KeySelectDlg();
|
||||
|
||||
void setIcon(IconType type, const QPixmap &icon);
|
||||
|
||||
Q_SIGNALS:
|
||||
void selected(const QCA::KeyStoreEntry &entry);
|
||||
void viewCertificate(const QCA::CertificateChain &chain);
|
||||
|
||||
protected slots:
|
||||
virtual void accept();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
friend class Private;
|
||||
Private *d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,951 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QtCore>
|
||||
#include <QtCrypto>
|
||||
#include <QtGui>
|
||||
|
||||
#include "certitem.h"
|
||||
#include "certviewdlg.h"
|
||||
#include "keyselectdlg.h"
|
||||
#include "pkcs11configdlg/pkcs11configdlg.h"
|
||||
#include "ui_mainwin.h"
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
#define VERSION "1.0.0"
|
||||
|
||||
class Icons
|
||||
{
|
||||
public:
|
||||
QPixmap cert, crl, keybundle, pgppub, pgpsec;
|
||||
};
|
||||
|
||||
Icons *g_icons = 0;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Operation
|
||||
//----------------------------------------------------------------------------
|
||||
class Operation : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Operation(QObject *parent = 0)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void error(const QString &str);
|
||||
};
|
||||
|
||||
static QString validityToString(QCA::Validity v)
|
||||
{
|
||||
QString s;
|
||||
switch (v) {
|
||||
case QCA::ValidityGood:
|
||||
s = Operation::tr("Validated");
|
||||
break;
|
||||
case QCA::ErrorRejected:
|
||||
s = Operation::tr("Root CA is marked to reject the specified purpose");
|
||||
break;
|
||||
case QCA::ErrorUntrusted:
|
||||
s = Operation::tr("Certificate not trusted for the required purpose");
|
||||
break;
|
||||
case QCA::ErrorSignatureFailed:
|
||||
s = Operation::tr("Invalid signature");
|
||||
break;
|
||||
case QCA::ErrorInvalidCA:
|
||||
s = Operation::tr("Invalid CA certificate");
|
||||
break;
|
||||
case QCA::ErrorInvalidPurpose:
|
||||
s = Operation::tr("Invalid certificate purpose");
|
||||
break;
|
||||
case QCA::ErrorSelfSigned:
|
||||
s = Operation::tr("Certificate is self-signed");
|
||||
break;
|
||||
case QCA::ErrorRevoked:
|
||||
s = Operation::tr("Certificate has been revoked");
|
||||
break;
|
||||
case QCA::ErrorPathLengthExceeded:
|
||||
s = Operation::tr("Maximum certificate chain length exceeded");
|
||||
break;
|
||||
case QCA::ErrorExpired:
|
||||
s = Operation::tr("Certificate has expired");
|
||||
break;
|
||||
case QCA::ErrorExpiredCA:
|
||||
s = Operation::tr("CA has expired");
|
||||
break;
|
||||
case QCA::ErrorValidityUnknown:
|
||||
default:
|
||||
s = Operation::tr("General certificate validation error");
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static QString smErrorToString(QCA::SecureMessage::Error e)
|
||||
{
|
||||
QString s;
|
||||
switch (e) {
|
||||
case QCA::SecureMessage::ErrorPassphrase:
|
||||
s = Operation::tr("Invalid passphrase.");
|
||||
break;
|
||||
case QCA::SecureMessage::ErrorFormat:
|
||||
s = Operation::tr("Bad input format.");
|
||||
break;
|
||||
case QCA::SecureMessage::ErrorSignerExpired:
|
||||
s = Operation::tr("Signer key is expired.");
|
||||
break;
|
||||
case QCA::SecureMessage::ErrorSignerInvalid:
|
||||
s = Operation::tr("Signer key is invalid.");
|
||||
break;
|
||||
case QCA::SecureMessage::ErrorEncryptExpired:
|
||||
s = Operation::tr("Encrypting key is expired.");
|
||||
break;
|
||||
case QCA::SecureMessage::ErrorEncryptUntrusted:
|
||||
s = Operation::tr("Encrypting key is untrusted.");
|
||||
break;
|
||||
case QCA::SecureMessage::ErrorEncryptInvalid:
|
||||
s = Operation::tr("Encrypting key is invalid.");
|
||||
break;
|
||||
case QCA::SecureMessage::ErrorNeedCard:
|
||||
s = Operation::tr("Card was needed but not found.");
|
||||
break;
|
||||
case QCA::SecureMessage::ErrorCertKeyMismatch:
|
||||
s = Operation::tr("Certificate and private key don't match.");
|
||||
break;
|
||||
case QCA::SecureMessage::ErrorUnknown:
|
||||
default:
|
||||
s = Operation::tr("General error.");
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static QString smsIdentityToString(const QCA::SecureMessageSignature &sig)
|
||||
{
|
||||
QString s;
|
||||
switch (sig.identityResult()) {
|
||||
case QCA::SecureMessageSignature::Valid:
|
||||
break;
|
||||
case QCA::SecureMessageSignature::InvalidSignature:
|
||||
s = Operation::tr("Invalid signature");
|
||||
break;
|
||||
case QCA::SecureMessageSignature::InvalidKey:
|
||||
s = Operation::tr("Invalid key: %1").arg(validityToString(sig.keyValidity()));
|
||||
break;
|
||||
case QCA::SecureMessageSignature::NoKey:
|
||||
s = Operation::tr("Key not found");
|
||||
break;
|
||||
default: // this should not really be possible
|
||||
s = Operation::tr("Unknown");
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
class SignOperation : public Operation
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
QByteArray in;
|
||||
CertItemStore *store;
|
||||
int id;
|
||||
QCA::CMS *cms;
|
||||
CertItemPrivateLoader *loader;
|
||||
QCA::SecureMessage *msg;
|
||||
int pending;
|
||||
|
||||
public:
|
||||
SignOperation(const QByteArray &_in, CertItemStore *_store, int _id, QCA::CMS *_cms, QObject *parent = 0)
|
||||
: Operation(parent)
|
||||
, in(_in)
|
||||
, store(_store)
|
||||
, id(_id)
|
||||
, cms(_cms)
|
||||
, msg(0)
|
||||
{
|
||||
loader = new CertItemPrivateLoader(store, this);
|
||||
connect(loader, SIGNAL(finished()), SLOT(loader_finished()));
|
||||
loader->start(id);
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void loadError();
|
||||
void finished(const QString &sig);
|
||||
|
||||
private Q_SLOTS:
|
||||
void loader_finished()
|
||||
{
|
||||
QCA::PrivateKey privateKey = loader->privateKey();
|
||||
delete loader;
|
||||
loader = 0;
|
||||
|
||||
if (privateKey.isNull()) {
|
||||
emit loadError();
|
||||
return;
|
||||
}
|
||||
|
||||
CertItem item = store->itemFromId(id);
|
||||
|
||||
QCA::SecureMessageKey signer;
|
||||
signer.setX509CertificateChain(item.certificateChain());
|
||||
signer.setX509PrivateKey(privateKey);
|
||||
|
||||
msg = new QCA::SecureMessage(cms);
|
||||
connect(msg, SIGNAL(bytesWritten(int)), SLOT(msg_bytesWritten(int)));
|
||||
connect(msg, SIGNAL(finished()), SLOT(msg_finished()));
|
||||
msg->setFormat(QCA::SecureMessage::Ascii);
|
||||
msg->setSigner(signer);
|
||||
msg->startSign(QCA::SecureMessage::Detached);
|
||||
|
||||
pending = 0;
|
||||
update();
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
QByteArray buf = in.mid(0, 16384 - pending); // 16k chunks
|
||||
in = in.mid(buf.size());
|
||||
pending += buf.size();
|
||||
msg->update(buf);
|
||||
}
|
||||
|
||||
void msg_bytesWritten(int x)
|
||||
{
|
||||
pending -= x;
|
||||
|
||||
if (in.isEmpty() && pending == 0)
|
||||
msg->end();
|
||||
else
|
||||
update();
|
||||
}
|
||||
|
||||
void msg_finished()
|
||||
{
|
||||
if (!msg->success()) {
|
||||
QString str = smErrorToString(msg->errorCode());
|
||||
delete msg;
|
||||
msg = 0;
|
||||
emit error(tr("Error during sign operation.\nReason: %1").arg(str));
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray result = msg->signature();
|
||||
delete msg;
|
||||
msg = 0;
|
||||
emit finished(QString::fromLatin1(result));
|
||||
}
|
||||
};
|
||||
|
||||
class VerifyOperation : public Operation
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
QByteArray in, sig;
|
||||
QCA::CMS *cms;
|
||||
QCA::SecureMessage *msg;
|
||||
int pending;
|
||||
|
||||
public:
|
||||
QCA::SecureMessageSignature signer;
|
||||
|
||||
VerifyOperation(const QByteArray &_in, const QByteArray &_sig, QCA::CMS *_cms, QObject *parent = 0)
|
||||
: Operation(parent)
|
||||
, in(_in)
|
||||
, sig(_sig)
|
||||
, cms(_cms)
|
||||
, msg(0)
|
||||
{
|
||||
msg = new QCA::SecureMessage(cms);
|
||||
connect(msg, SIGNAL(bytesWritten(int)), SLOT(msg_bytesWritten(int)));
|
||||
connect(msg, SIGNAL(finished()), SLOT(msg_finished()));
|
||||
msg->setFormat(QCA::SecureMessage::Ascii);
|
||||
msg->startVerify(sig);
|
||||
|
||||
pending = 0;
|
||||
update();
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void finished();
|
||||
|
||||
private Q_SLOTS:
|
||||
void update()
|
||||
{
|
||||
QByteArray buf = in.mid(0, 16384 - pending); // 16k chunks
|
||||
in = in.mid(buf.size());
|
||||
pending += buf.size();
|
||||
msg->update(buf);
|
||||
}
|
||||
|
||||
void msg_bytesWritten(int x)
|
||||
{
|
||||
pending -= x;
|
||||
|
||||
if (in.isEmpty() && pending == 0)
|
||||
msg->end();
|
||||
else
|
||||
update();
|
||||
}
|
||||
|
||||
void msg_finished()
|
||||
{
|
||||
if (!msg->success()) {
|
||||
QString str = smErrorToString(msg->errorCode());
|
||||
delete msg;
|
||||
msg = 0;
|
||||
emit error(tr("Error during verify operation.\nReason: %1").arg(str));
|
||||
return;
|
||||
}
|
||||
|
||||
signer = msg->signer();
|
||||
delete msg;
|
||||
msg = 0;
|
||||
|
||||
if (signer.identityResult() != QCA::SecureMessageSignature::Valid) {
|
||||
QString str = smsIdentityToString(signer);
|
||||
emit error(tr("Verification failed!\nReason: %1").arg(str));
|
||||
return;
|
||||
}
|
||||
|
||||
emit finished();
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// MainWin
|
||||
//----------------------------------------------------------------------------
|
||||
static QString get_fingerprint(const QCA::Certificate &cert)
|
||||
{
|
||||
QString hex = QCA::Hash("sha1").hashToString(cert.toDER());
|
||||
QString out;
|
||||
for (int n = 0; n < hex.count(); ++n) {
|
||||
if (n != 0 && n % 2 == 0)
|
||||
out += ':';
|
||||
out += hex[n];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
class MainWin : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
Ui_MainWin ui;
|
||||
CertItemStore *users, *roots;
|
||||
QCA::CMS *cms;
|
||||
Operation *op;
|
||||
QAction *actionView, *actionRename, *actionRemove;
|
||||
QCA::Certificate self_signed_verify_cert;
|
||||
int auto_import_req_id;
|
||||
|
||||
public:
|
||||
MainWin(QWidget *parent = 0)
|
||||
: QMainWindow(parent)
|
||||
, op(0)
|
||||
, auto_import_req_id(-1)
|
||||
{
|
||||
ui.setupUi(this);
|
||||
|
||||
g_icons = new Icons;
|
||||
g_icons->cert = QPixmap(":/gfx/icons/cert16.png");
|
||||
g_icons->crl = QPixmap(":/gfx/icons/crl16.png");
|
||||
g_icons->keybundle = QPixmap(":/gfx/icons/keybundle16.png");
|
||||
g_icons->pgppub = QPixmap(":/gfx/icons/publickey16.png");
|
||||
g_icons->pgpsec = QPixmap(":/gfx/icons/keypair16.png");
|
||||
if (g_icons->cert.isNull() || g_icons->crl.isNull() || g_icons->keybundle.isNull() ||
|
||||
g_icons->pgppub.isNull() || g_icons->pgpsec.isNull())
|
||||
printf("Warning: not all icons loaded\n");
|
||||
|
||||
users = new CertItemStore(this);
|
||||
roots = new CertItemStore(this);
|
||||
|
||||
setIcons(users);
|
||||
setIcons(roots);
|
||||
|
||||
connect(users, SIGNAL(addSuccess(int, int)), SLOT(users_addSuccess(int, int)));
|
||||
connect(users, SIGNAL(addFailed(int)), SLOT(users_addFailed(int)));
|
||||
|
||||
actionView = new QAction(tr("&View"), this);
|
||||
actionRename = new QAction(tr("Re&name"), this);
|
||||
actionRemove = new QAction(tr("Rem&ove"), this);
|
||||
|
||||
connect(ui.actionLoadIdentityFile, SIGNAL(triggered()), SLOT(load_file()));
|
||||
connect(ui.actionLoadIdentityEntry, SIGNAL(triggered()), SLOT(load_device()));
|
||||
connect(ui.actionLoadAuthority, SIGNAL(triggered()), SLOT(load_root()));
|
||||
connect(ui.actionConfigurePkcs11, SIGNAL(triggered()), SLOT(mod_config()));
|
||||
connect(ui.actionQuit, SIGNAL(triggered()), SLOT(close()));
|
||||
connect(ui.actionAbout, SIGNAL(triggered()), SLOT(about()));
|
||||
connect(ui.pb_sign, SIGNAL(clicked()), SLOT(do_sign()));
|
||||
connect(ui.pb_verify, SIGNAL(clicked()), SLOT(do_verify()));
|
||||
|
||||
connect(actionView, SIGNAL(triggered()), SLOT(item_view()));
|
||||
connect(actionRename, SIGNAL(triggered()), SLOT(item_rename()));
|
||||
connect(actionRemove, SIGNAL(triggered()), SLOT(item_remove()));
|
||||
|
||||
ui.pb_sign->setEnabled(false);
|
||||
|
||||
ui.lv_users->setModel(users);
|
||||
connect(ui.lv_users->selectionModel(),
|
||||
SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
|
||||
SLOT(users_selectionChanged(const QItemSelection &, const QItemSelection &)));
|
||||
|
||||
ui.lv_users->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui.lv_users,
|
||||
SIGNAL(customContextMenuRequested(const QPoint &)),
|
||||
SLOT(users_customContextMenuRequested(const QPoint &)));
|
||||
|
||||
ui.lv_authorities->setModel(roots);
|
||||
|
||||
ui.lv_authorities->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui.lv_authorities,
|
||||
SIGNAL(customContextMenuRequested(const QPoint &)),
|
||||
SLOT(roots_customContextMenuRequested(const QPoint &)));
|
||||
|
||||
cms = new QCA::CMS(this);
|
||||
|
||||
QStringList ulist, rlist;
|
||||
{
|
||||
QSettings settings("Affinix", "CMS Signer");
|
||||
ulist = settings.value("users").toStringList();
|
||||
rlist = settings.value("roots").toStringList();
|
||||
}
|
||||
|
||||
users->load(ulist);
|
||||
roots->load(rlist);
|
||||
}
|
||||
|
||||
~MainWin()
|
||||
{
|
||||
QStringList ulist = users->save();
|
||||
QStringList rlist = roots->save();
|
||||
|
||||
QSettings settings("Affinix", "CMS Signer");
|
||||
settings.setValue("users", ulist);
|
||||
settings.setValue("roots", rlist);
|
||||
|
||||
delete g_icons;
|
||||
g_icons = 0;
|
||||
}
|
||||
|
||||
void setIcons(CertItemStore *store)
|
||||
{
|
||||
store->setIcon(CertItemStore::IconCert, g_icons->cert);
|
||||
store->setIcon(CertItemStore::IconCrl, g_icons->crl);
|
||||
store->setIcon(CertItemStore::IconKeyBundle, g_icons->keybundle);
|
||||
store->setIcon(CertItemStore::IconPgpPub, g_icons->pgppub);
|
||||
store->setIcon(CertItemStore::IconPgpSec, g_icons->pgpsec);
|
||||
}
|
||||
|
||||
QCA::CertificateCollection allCerts()
|
||||
{
|
||||
QCA::CertificateCollection col;
|
||||
|
||||
// system store
|
||||
col += QCA::systemStore();
|
||||
|
||||
// additional roots configured in application
|
||||
foreach (const CertItem &i, roots->items())
|
||||
col.addCertificate(i.certificateChain().primary());
|
||||
|
||||
// user chains
|
||||
foreach (const CertItem &i, users->items()) {
|
||||
foreach (const QCA::Certificate &cert, i.certificateChain())
|
||||
col.addCertificate(cert);
|
||||
}
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
QCA::CertificateChain complete(const QCA::CertificateChain &chain)
|
||||
{
|
||||
return chain.complete(allCerts().certificates());
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void load_file()
|
||||
{
|
||||
QString fileName =
|
||||
QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("X.509 Identities (*.p12 *.pfx)"));
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
|
||||
setEnabled(false);
|
||||
users->addFromFile(fileName);
|
||||
}
|
||||
|
||||
void load_device()
|
||||
{
|
||||
KeySelectDlg *w = new KeySelectDlg(this);
|
||||
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
w->setWindowModality(Qt::WindowModal);
|
||||
connect(
|
||||
w, SIGNAL(selected(const QCA::KeyStoreEntry &)), SLOT(load_device_finished(const QCA::KeyStoreEntry &)));
|
||||
connect(w,
|
||||
SIGNAL(viewCertificate(const QCA::CertificateChain &)),
|
||||
SLOT(keyselect_viewCertificate(const QCA::CertificateChain &)));
|
||||
w->setIcon(KeySelectDlg::IconCert, g_icons->cert);
|
||||
w->setIcon(KeySelectDlg::IconCrl, g_icons->crl);
|
||||
w->setIcon(KeySelectDlg::IconKeyBundle, g_icons->keybundle);
|
||||
w->setIcon(KeySelectDlg::IconPgpPub, g_icons->pgppub);
|
||||
w->setIcon(KeySelectDlg::IconPgpSec, g_icons->pgpsec);
|
||||
w->show();
|
||||
}
|
||||
|
||||
void load_device_finished(const QCA::KeyStoreEntry &entry)
|
||||
{
|
||||
users->addFromKeyStore(entry);
|
||||
}
|
||||
|
||||
void load_root()
|
||||
{
|
||||
QString fileName =
|
||||
QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("X.509 Certificates (*.pem *.crt)"));
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
|
||||
QCA::Certificate cert = QCA::Certificate::fromPEMFile(fileName);
|
||||
if (cert.isNull()) {
|
||||
QMessageBox::information(this, tr("Error"), tr("Error opening certificate file."));
|
||||
return;
|
||||
}
|
||||
|
||||
roots->addUser(cert);
|
||||
}
|
||||
|
||||
void users_addSuccess(int req_id, int id)
|
||||
{
|
||||
if (req_id == auto_import_req_id) {
|
||||
auto_import_req_id = -1;
|
||||
|
||||
CertItem i = users->itemFromId(id);
|
||||
|
||||
QMessageBox::information(this,
|
||||
tr("User added"),
|
||||
tr("This signature was made by a previously unknown user, and so the "
|
||||
"user has now been added to the keyring as \"%1\".")
|
||||
.arg(i.name()));
|
||||
|
||||
verify_next();
|
||||
return;
|
||||
}
|
||||
|
||||
ui.lv_users->selectionModel()->select(users->index(users->rowFromId(id)),
|
||||
QItemSelectionModel::Clear | QItemSelectionModel::Select |
|
||||
QItemSelectionModel::Current);
|
||||
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
void users_addFailed(int req_id)
|
||||
{
|
||||
Q_UNUSED(req_id);
|
||||
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
void mod_config()
|
||||
{
|
||||
if (!Pkcs11ConfigDlg::isSupported()) {
|
||||
QMessageBox::information(
|
||||
this, tr("Error"), tr("No provider available supporting standard PKCS#11 configuration."));
|
||||
return;
|
||||
}
|
||||
|
||||
Pkcs11ConfigDlg *w = new Pkcs11ConfigDlg(this);
|
||||
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
w->setWindowModality(Qt::WindowModal);
|
||||
w->show();
|
||||
}
|
||||
|
||||
void users_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
||||
{
|
||||
Q_UNUSED(deselected);
|
||||
|
||||
int at = -1;
|
||||
if (!selected.indexes().isEmpty()) {
|
||||
QModelIndex index = selected.indexes().first();
|
||||
at = index.row();
|
||||
}
|
||||
|
||||
bool usable = false;
|
||||
if (at != -1 && users->itemFromRow(at).isUsable())
|
||||
usable = true;
|
||||
|
||||
if (usable && !ui.pb_sign->isEnabled())
|
||||
ui.pb_sign->setEnabled(true);
|
||||
else if (!usable && ui.pb_sign->isEnabled())
|
||||
ui.pb_sign->setEnabled(false);
|
||||
}
|
||||
|
||||
void item_view()
|
||||
{
|
||||
if (ui.lv_users->hasFocus()) {
|
||||
QItemSelection selection = ui.lv_users->selectionModel()->selection();
|
||||
if (selection.indexes().isEmpty())
|
||||
return;
|
||||
QModelIndex index = selection.indexes().first();
|
||||
users_view(index.row());
|
||||
} else // lv_authorities
|
||||
{
|
||||
QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
|
||||
if (selection.indexes().isEmpty())
|
||||
return;
|
||||
QModelIndex index = selection.indexes().first();
|
||||
roots_view(index.row());
|
||||
}
|
||||
}
|
||||
|
||||
void item_rename()
|
||||
{
|
||||
if (ui.lv_users->hasFocus()) {
|
||||
QItemSelection selection = ui.lv_users->selectionModel()->selection();
|
||||
if (selection.indexes().isEmpty())
|
||||
return;
|
||||
QModelIndex index = selection.indexes().first();
|
||||
users_rename(index.row());
|
||||
} else // lv_authorities
|
||||
{
|
||||
QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
|
||||
if (selection.indexes().isEmpty())
|
||||
return;
|
||||
QModelIndex index = selection.indexes().first();
|
||||
roots_rename(index.row());
|
||||
}
|
||||
}
|
||||
|
||||
void item_remove()
|
||||
{
|
||||
if (ui.lv_users->hasFocus()) {
|
||||
QItemSelection selection = ui.lv_users->selectionModel()->selection();
|
||||
if (selection.indexes().isEmpty())
|
||||
return;
|
||||
QModelIndex index = selection.indexes().first();
|
||||
users_remove(index.row());
|
||||
} else // lv_authorities
|
||||
{
|
||||
QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
|
||||
if (selection.indexes().isEmpty())
|
||||
return;
|
||||
QModelIndex index = selection.indexes().first();
|
||||
roots_remove(index.row());
|
||||
}
|
||||
}
|
||||
|
||||
void users_view(int at)
|
||||
{
|
||||
CertItem i = users->itemFromRow(at);
|
||||
CertViewDlg *w = new CertViewDlg(complete(i.certificateChain()), this);
|
||||
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
w->show();
|
||||
}
|
||||
|
||||
void users_rename(int at)
|
||||
{
|
||||
QModelIndex index = users->index(at);
|
||||
ui.lv_users->setFocus();
|
||||
ui.lv_users->setCurrentIndex(index);
|
||||
ui.lv_users->selectionModel()->select(
|
||||
index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
|
||||
ui.lv_users->edit(index);
|
||||
}
|
||||
|
||||
void users_remove(int at)
|
||||
{
|
||||
users->removeItem(users->idFromRow(at));
|
||||
}
|
||||
|
||||
void roots_view(int at)
|
||||
{
|
||||
CertItem i = roots->itemFromRow(at);
|
||||
CertViewDlg *w = new CertViewDlg(complete(i.certificateChain()), this);
|
||||
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
w->show();
|
||||
}
|
||||
|
||||
void roots_rename(int at)
|
||||
{
|
||||
QModelIndex index = roots->index(at);
|
||||
ui.lv_authorities->setFocus();
|
||||
ui.lv_authorities->setCurrentIndex(index);
|
||||
ui.lv_authorities->selectionModel()->select(
|
||||
index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
|
||||
ui.lv_authorities->edit(index);
|
||||
}
|
||||
|
||||
void roots_remove(int at)
|
||||
{
|
||||
roots->removeItem(roots->idFromRow(at));
|
||||
}
|
||||
|
||||
void keyselect_viewCertificate(const QCA::CertificateChain &chain)
|
||||
{
|
||||
CertViewDlg *w = new CertViewDlg(complete(chain), (QWidget *)sender());
|
||||
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
w->show();
|
||||
}
|
||||
|
||||
void users_customContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
QItemSelection selection = ui.lv_users->selectionModel()->selection();
|
||||
if (selection.indexes().isEmpty())
|
||||
return;
|
||||
|
||||
QMenu menu(this);
|
||||
menu.addAction(actionView);
|
||||
menu.addAction(actionRename);
|
||||
menu.addAction(actionRemove);
|
||||
menu.exec(ui.lv_users->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void roots_customContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
QItemSelection selection = ui.lv_authorities->selectionModel()->selection();
|
||||
if (selection.indexes().isEmpty())
|
||||
return;
|
||||
|
||||
QMenu menu(this);
|
||||
menu.addAction(actionView);
|
||||
menu.addAction(actionRename);
|
||||
menu.addAction(actionRemove);
|
||||
menu.exec(ui.lv_authorities->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void do_sign()
|
||||
{
|
||||
QItemSelection selection = ui.lv_users->selectionModel()->selection();
|
||||
if (selection.indexes().isEmpty())
|
||||
return;
|
||||
QModelIndex index = selection.indexes().first();
|
||||
int at = index.row();
|
||||
|
||||
setEnabled(false);
|
||||
|
||||
op = new SignOperation(ui.te_data->toPlainText().toUtf8(), users, users->idFromRow(at), cms, this);
|
||||
connect(op, SIGNAL(loadError()), SLOT(sign_loadError()));
|
||||
connect(op, SIGNAL(finished(const QString &)), SLOT(sign_finished(const QString &)));
|
||||
connect(op, SIGNAL(error(const QString &)), SLOT(sign_error(const QString &)));
|
||||
}
|
||||
|
||||
void do_verify()
|
||||
{
|
||||
// prepare root certs
|
||||
QCA::CertificateCollection col;
|
||||
|
||||
// system store
|
||||
col += QCA::systemStore();
|
||||
|
||||
// additional roots configured in application
|
||||
foreach (const CertItem &i, roots->items())
|
||||
col.addCertificate(i.certificateChain().primary());
|
||||
|
||||
// consider self-signed users as roots
|
||||
// (it is therefore not possible with this application to
|
||||
// have people in your keyring that you don't trust)
|
||||
foreach (const CertItem &i, users->items()) {
|
||||
QCA::Certificate cert = i.certificateChain().primary();
|
||||
if (cert.isSelfSigned())
|
||||
col.addCertificate(cert);
|
||||
}
|
||||
|
||||
// the self signed verify cert, if applicable
|
||||
if (!self_signed_verify_cert.isNull()) {
|
||||
col.addCertificate(self_signed_verify_cert);
|
||||
self_signed_verify_cert = QCA::Certificate();
|
||||
}
|
||||
|
||||
cms->setTrustedCertificates(col);
|
||||
|
||||
setEnabled(false);
|
||||
|
||||
op = new VerifyOperation(ui.te_data->toPlainText().toUtf8(), ui.te_sig->toPlainText().toUtf8(), cms, this);
|
||||
connect(op, SIGNAL(finished()), SLOT(verify_finished()));
|
||||
connect(op, SIGNAL(error(const QString &)), SLOT(verify_error(const QString &)));
|
||||
}
|
||||
|
||||
void about()
|
||||
{
|
||||
int ver = qcaVersion();
|
||||
int maj = (ver >> 16) & 0xff;
|
||||
int min = (ver >> 8) & 0xff;
|
||||
int bug = ver & 0xff;
|
||||
QString verstr;
|
||||
verstr.sprintf("%d.%d.%d", maj, min, bug);
|
||||
|
||||
QString str;
|
||||
str += tr("CMS Signer version %1 by Justin Karneges").arg(VERSION) + '\n';
|
||||
str += tr("A simple tool for creating and verifying digital signatures.") + '\n';
|
||||
str += '\n';
|
||||
str += tr("Using QCA version %1").arg(verstr) + '\n';
|
||||
str += '\n';
|
||||
str += tr("Icons by Jason Kim") + '\n';
|
||||
|
||||
QCA::ProviderList list = QCA::providers();
|
||||
foreach (QCA::Provider *p, list) {
|
||||
QString credit = p->credit();
|
||||
if (!credit.isEmpty()) {
|
||||
str += '\n';
|
||||
str += credit;
|
||||
}
|
||||
}
|
||||
|
||||
QMessageBox::about(this, tr("About CMS Signer"), str);
|
||||
}
|
||||
|
||||
void sign_loadError()
|
||||
{
|
||||
delete op;
|
||||
op = 0;
|
||||
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
void sign_finished(const QString &sig)
|
||||
{
|
||||
delete op;
|
||||
op = 0;
|
||||
|
||||
ui.te_sig->setPlainText(sig);
|
||||
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
void sign_error(const QString &msg)
|
||||
{
|
||||
delete op;
|
||||
op = 0;
|
||||
|
||||
setEnabled(true);
|
||||
|
||||
QMessageBox::information(this, tr("Error"), msg);
|
||||
}
|
||||
|
||||
void verify_finished()
|
||||
{
|
||||
QCA::SecureMessageSignature signer = ((VerifyOperation *)op)->signer;
|
||||
delete op;
|
||||
op = 0;
|
||||
|
||||
// import the cert?
|
||||
QCA::SecureMessageKey skey = signer.key();
|
||||
if (!skey.isNull()) {
|
||||
QCA::CertificateChain chain = skey.x509CertificateChain();
|
||||
|
||||
int at = -1;
|
||||
QList<CertItem> items = users->items();
|
||||
for (int n = 0; n < items.count(); ++n) {
|
||||
const CertItem &i = items[n];
|
||||
if (i.certificateChain().primary() == chain.primary()) {
|
||||
at = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add
|
||||
if (at == -1) {
|
||||
auto_import_req_id = users->addUser(chain);
|
||||
return;
|
||||
}
|
||||
// update
|
||||
else {
|
||||
users->updateChain(users->idFromRow(at), chain);
|
||||
}
|
||||
}
|
||||
|
||||
verify_next();
|
||||
}
|
||||
|
||||
void verify_next()
|
||||
{
|
||||
setEnabled(true);
|
||||
|
||||
QMessageBox::information(this, tr("Verify"), tr("Signature verified successfully."));
|
||||
}
|
||||
|
||||
void verify_error(const QString &msg)
|
||||
{
|
||||
QCA::SecureMessageSignature signer = ((VerifyOperation *)op)->signer;
|
||||
delete op;
|
||||
op = 0;
|
||||
|
||||
QCA::SecureMessageKey skey = signer.key();
|
||||
if (signer.keyValidity() == QCA::ErrorSelfSigned && !skey.isNull()) {
|
||||
QCA::CertificateChain chain = skey.x509CertificateChain();
|
||||
if (chain.count() == 1 && chain.primary().isSelfSigned()) {
|
||||
QCA::Certificate cert = chain.primary();
|
||||
|
||||
int ret = QMessageBox::warning(
|
||||
this,
|
||||
tr("Self-signed certificate"),
|
||||
tr("<qt>The signature is made by an unknown user, and the certificate is self-signed.<br>\n"
|
||||
"<br>\n"
|
||||
"<nobr>Common Name: %1</nobr><br>\n"
|
||||
"<nobr>SHA1 Fingerprint: %2</nobr><br>\n"
|
||||
"<br>\n"
|
||||
"Trust the certificate?</qt>")
|
||||
.arg(cert.commonName(), get_fingerprint(cert)),
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::No);
|
||||
|
||||
if (ret == QMessageBox::Yes) {
|
||||
self_signed_verify_cert = cert;
|
||||
do_verify();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setEnabled(true);
|
||||
|
||||
QMessageBox::information(this, tr("Error"), msg);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCA::Initializer qcaInit;
|
||||
QApplication qapp(argc, argv);
|
||||
|
||||
qapp.setApplicationName(MainWin::tr("CMS Signer"));
|
||||
|
||||
if (!QCA::isSupported("cert,crl,cms")) {
|
||||
QMessageBox::critical(
|
||||
0,
|
||||
qapp.applicationName() + ": " + MainWin::tr("Error"),
|
||||
MainWin::tr(
|
||||
"No support for CMS is available. Please install an appropriate QCA plugin, such as qca-ossl."));
|
||||
return 1;
|
||||
}
|
||||
|
||||
QCA::KeyStoreManager::start();
|
||||
|
||||
MainWin mainWin;
|
||||
mainWin.show();
|
||||
return qapp.exec();
|
||||
}
|
||||
|
||||
#include "main.moc"
|
||||
@@ -0,0 +1,226 @@
|
||||
<ui version="4.0" >
|
||||
<class>MainWin</class>
|
||||
<widget class="QMainWindow" name="MainWin" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>577</width>
|
||||
<height>501</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>CMS Signer</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget" >
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget" >
|
||||
<property name="currentIndex" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab" >
|
||||
<attribute name="title" >
|
||||
<string>Keyring</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListView" name="lv_users" />
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2" >
|
||||
<attribute name="title" >
|
||||
<string>Authorities</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListView" name="lv_authorities" />
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<string>Text:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="te_data" />
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3" >
|
||||
<property name="text" >
|
||||
<string>Signature:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="te_sig" />
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pb_sign" >
|
||||
<property name="text" >
|
||||
<string>&Sign</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pb_verify" >
|
||||
<property name="text" >
|
||||
<string>&Verify</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>577</width>
|
||||
<height>29</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuHelp" >
|
||||
<property name="title" >
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="actionAbout" />
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuFile" >
|
||||
<property name="title" >
|
||||
<string>&File</string>
|
||||
</property>
|
||||
<addaction name="actionLoadIdentityFile" />
|
||||
<addaction name="actionLoadIdentityEntry" />
|
||||
<addaction name="actionLoadAuthority" />
|
||||
<addaction name="separator" />
|
||||
<addaction name="actionConfigurePkcs11" />
|
||||
<addaction name="separator" />
|
||||
<addaction name="actionQuit" />
|
||||
</widget>
|
||||
<addaction name="menuFile" />
|
||||
<addaction name="menuHelp" />
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar" />
|
||||
<action name="actionLoadIdentityFile" >
|
||||
<property name="text" >
|
||||
<string>Load Identity From &File...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLoadIdentityEntry" >
|
||||
<property name="text" >
|
||||
<string>Load Identity From &Storage Device...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionQuit" >
|
||||
<property name="text" >
|
||||
<string>&Quit</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout" >
|
||||
<property name="text" >
|
||||
<string>About CMS Signer</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionConfigurePkcs11" >
|
||||
<property name="text" >
|
||||
<string>Configure &PKCS#11 Modules...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLoadAuthority" >
|
||||
<property name="text" >
|
||||
<string>Load &Authority...</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>tabWidget</tabstop>
|
||||
<tabstop>lv_users</tabstop>
|
||||
<tabstop>lv_authorities</tabstop>
|
||||
<tabstop>te_data</tabstop>
|
||||
<tabstop>te_sig</tabstop>
|
||||
<tabstop>pb_sign</tabstop>
|
||||
<tabstop>pb_verify</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="cmssigner.qrc" />
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,624 @@
|
||||
<ui version="4.0" >
|
||||
<class>Pkcs11Config</class>
|
||||
<widget class="QDialog" name="Pkcs11Config" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>570</width>
|
||||
<height>458</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>PKCS#11 Module Configuration</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_general" >
|
||||
<property name="title" >
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="ck_allowroot" >
|
||||
<property name="text" >
|
||||
<string>Allow Loading of Root CAs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="ck_allowprotected" >
|
||||
<property name="text" >
|
||||
<string>Allow Protected Authentication</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3" >
|
||||
<property name="text" >
|
||||
<string>Log Level:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="sb_loglevel" />
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_pincache" >
|
||||
<property name="title" >
|
||||
<string>Remember PIN</string>
|
||||
</property>
|
||||
<property name="checkable" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rb_pincache_nolimit" >
|
||||
<property name="text" >
|
||||
<string>Until Session Ends</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rb_pincache_time" >
|
||||
<property name="text" >
|
||||
<string>For a Time (in Seconds):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="sb_pincache_time" >
|
||||
<property name="maximum" >
|
||||
<number>86400</number>
|
||||
</property>
|
||||
<property name="minimum" >
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value" >
|
||||
<number>300</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_modules" >
|
||||
<property name="title" >
|
||||
<string>PKCS#11 Modules</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListView" name="lv_modules" />
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pb_add" >
|
||||
<property name="text" >
|
||||
<string>&New</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pb_remove" >
|
||||
<property name="text" >
|
||||
<string>Rem&ove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tb_details" >
|
||||
<property name="currentIndex" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab" >
|
||||
<attribute name="title" >
|
||||
<string>File</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="lb_file" >
|
||||
<property name="text" >
|
||||
<string>Module File:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="le_library" />
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pb_browse" >
|
||||
<property name="text" >
|
||||
<string>Browse For Module File...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2" >
|
||||
<attribute name="title" >
|
||||
<string>Slot Events</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<string>Method:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="cb_slotmethod" >
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Determine Automatically</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Trigger</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text" >
|
||||
<string>Polling</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_poll" >
|
||||
<property name="title" >
|
||||
<string>Polling Interval</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rb_polldefault" >
|
||||
<property name="text" >
|
||||
<string>No Preference, Use Internal Default</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rb_pollcustom" >
|
||||
<property name="text" >
|
||||
<string>Custom (in Seconds):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="sb_pollcustom" >
|
||||
<property name="maximum" >
|
||||
<number>86400</number>
|
||||
</property>
|
||||
<property name="minimum" >
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_3" >
|
||||
<attribute name="title" >
|
||||
<string>Advanced</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="ck_modallowprotected" >
|
||||
<property name="text" >
|
||||
<string>Allow Protected Authentication</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="ck_certprivate" >
|
||||
<property name="text" >
|
||||
<string>Certificates Stored As Private Objects</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_keymodes" >
|
||||
<property name="title" >
|
||||
<string>Private Key Modes</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="ck_modeauto" >
|
||||
<property name="text" >
|
||||
<string>Determine Automatically</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0" >
|
||||
<widget class="QCheckBox" name="ck_modesign" >
|
||||
<property name="text" >
|
||||
<string>Sign</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QCheckBox" name="ck_modeunwrap" >
|
||||
<property name="text" >
|
||||
<string>Unwrap</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" >
|
||||
<widget class="QCheckBox" name="ck_modedecrypt" >
|
||||
<property name="text" >
|
||||
<string>Decrypt</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QCheckBox" name="ck_modesignrecover" >
|
||||
<property name="text" >
|
||||
<string>Sign Recover</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons" >
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>ck_allowroot</tabstop>
|
||||
<tabstop>ck_allowprotected</tabstop>
|
||||
<tabstop>sb_loglevel</tabstop>
|
||||
<tabstop>gb_pincache</tabstop>
|
||||
<tabstop>rb_pincache_nolimit</tabstop>
|
||||
<tabstop>rb_pincache_time</tabstop>
|
||||
<tabstop>sb_pincache_time</tabstop>
|
||||
<tabstop>lv_modules</tabstop>
|
||||
<tabstop>pb_add</tabstop>
|
||||
<tabstop>pb_remove</tabstop>
|
||||
<tabstop>tb_details</tabstop>
|
||||
<tabstop>le_library</tabstop>
|
||||
<tabstop>pb_browse</tabstop>
|
||||
<tabstop>cb_slotmethod</tabstop>
|
||||
<tabstop>rb_polldefault</tabstop>
|
||||
<tabstop>rb_pollcustom</tabstop>
|
||||
<tabstop>sb_pollcustom</tabstop>
|
||||
<tabstop>ck_modallowprotected</tabstop>
|
||||
<tabstop>ck_certprivate</tabstop>
|
||||
<tabstop>ck_modeauto</tabstop>
|
||||
<tabstop>ck_modesign</tabstop>
|
||||
<tabstop>ck_modesignrecover</tabstop>
|
||||
<tabstop>ck_modedecrypt</tabstop>
|
||||
<tabstop>ck_modeunwrap</tabstop>
|
||||
<tabstop>buttonBox</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Pkcs11Config</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>345</x>
|
||||
<y>448</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>320</x>
|
||||
<y>137</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Pkcs11Config</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>370</x>
|
||||
<y>448</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>363</x>
|
||||
<y>135</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -0,0 +1,774 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "pkcs11configdlg.h"
|
||||
|
||||
#include "ui_pkcs11config.h"
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QtCore>
|
||||
#include <QtCrypto>
|
||||
#include <QtGui>
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Pkcs11ProviderConfig
|
||||
//----------------------------------------------------------------------------
|
||||
class Pkcs11ProviderConfig
|
||||
{
|
||||
public:
|
||||
bool allow_protected_authentication;
|
||||
bool cert_private;
|
||||
bool enabled;
|
||||
QString library;
|
||||
QString name;
|
||||
int private_mask;
|
||||
QString slotevent_method;
|
||||
int slotevent_timeout;
|
||||
|
||||
Pkcs11ProviderConfig()
|
||||
: allow_protected_authentication(true)
|
||||
, cert_private(false)
|
||||
, enabled(false)
|
||||
, private_mask(0)
|
||||
, slotevent_method("auto")
|
||||
, slotevent_timeout(0)
|
||||
{
|
||||
}
|
||||
|
||||
QVariantMap toVariantMap() const
|
||||
{
|
||||
QVariantMap out;
|
||||
out["allow_protected_authentication"] = allow_protected_authentication;
|
||||
out["cert_private"] = cert_private;
|
||||
out["enabled"] = enabled;
|
||||
out["library"] = library;
|
||||
out["name"] = name;
|
||||
out["private_mask"] = private_mask;
|
||||
out["slotevent_method"] = slotevent_method;
|
||||
out["slotevent_timeout"] = slotevent_timeout;
|
||||
return out;
|
||||
}
|
||||
|
||||
bool fromVariantMap(const QVariantMap &in)
|
||||
{
|
||||
allow_protected_authentication = in["allow_protected_authentication"].toBool();
|
||||
cert_private = in["cert_private"].toBool();
|
||||
enabled = in["enabled"].toBool();
|
||||
library = in["library"].toString();
|
||||
name = in["name"].toString();
|
||||
private_mask = in["private_mask"].toInt();
|
||||
slotevent_method = in["slotevent_method"].toString();
|
||||
slotevent_timeout = in["slotevent_timeout"].toInt();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Pkcs11Config
|
||||
//----------------------------------------------------------------------------
|
||||
class Pkcs11Config
|
||||
{
|
||||
public:
|
||||
bool allow_load_rootca;
|
||||
bool allow_protected_authentication;
|
||||
int log_level;
|
||||
int pin_cache;
|
||||
QList<Pkcs11ProviderConfig> providers;
|
||||
|
||||
QVariantMap orig_config;
|
||||
|
||||
Pkcs11Config()
|
||||
: allow_load_rootca(false)
|
||||
, allow_protected_authentication(true)
|
||||
, log_level(0)
|
||||
, pin_cache(-1)
|
||||
{
|
||||
}
|
||||
|
||||
QVariantMap toVariantMap() const
|
||||
{
|
||||
QVariantMap out = orig_config;
|
||||
|
||||
// form type
|
||||
out["formtype"] = "http://affinix.com/qca/forms/qca-pkcs11#1.0";
|
||||
|
||||
// base settings
|
||||
out["allow_load_rootca"] = allow_load_rootca;
|
||||
out["allow_protected_authentication"] = allow_protected_authentication;
|
||||
out["log_level"] = log_level;
|
||||
out["pin_cache"] = pin_cache;
|
||||
|
||||
// provider settings (always write at least 10 providers)
|
||||
for (int n = 0; n < 10 || n < providers.count(); ++n) {
|
||||
QString prefix = QString().sprintf("provider_%02d_", n);
|
||||
|
||||
Pkcs11ProviderConfig provider;
|
||||
if (n < providers.count())
|
||||
provider = providers[n];
|
||||
|
||||
QVariantMap subconfig = provider.toVariantMap();
|
||||
QMapIterator<QString, QVariant> it(subconfig);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
out.insert(prefix + it.key(), it.value());
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
bool fromVariantMap(const QVariantMap &in)
|
||||
{
|
||||
if (in["formtype"] != "http://affinix.com/qca/forms/qca-pkcs11#1.0")
|
||||
return false;
|
||||
|
||||
allow_load_rootca = in["allow_load_rootca"].toBool();
|
||||
allow_protected_authentication = in["allow_protected_authentication"].toBool();
|
||||
log_level = in["log_level"].toInt();
|
||||
pin_cache = in["pin_cache"].toInt();
|
||||
|
||||
for (int n = 0;; ++n) {
|
||||
QString prefix = QString().sprintf("provider_%02d_", n);
|
||||
|
||||
// collect all key/values with this prefix into a
|
||||
// a separate container, leaving out the prefix
|
||||
// from the keys.
|
||||
QVariantMap subconfig;
|
||||
QMapIterator<QString, QVariant> it(in);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
if (it.key().startsWith(prefix))
|
||||
subconfig.insert(it.key().mid(prefix.length()), it.value());
|
||||
}
|
||||
|
||||
// if there are no config items with this prefix, we're done
|
||||
if (subconfig.isEmpty())
|
||||
break;
|
||||
|
||||
Pkcs11ProviderConfig provider;
|
||||
if (!provider.fromVariantMap(subconfig))
|
||||
return false;
|
||||
|
||||
// skip unnamed entries
|
||||
if (provider.name.isEmpty())
|
||||
continue;
|
||||
|
||||
// skip duplicate entries
|
||||
bool have_name_already = false;
|
||||
foreach (const Pkcs11ProviderConfig &i, providers) {
|
||||
if (i.name == provider.name) {
|
||||
have_name_already = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (have_name_already)
|
||||
continue;
|
||||
|
||||
providers += provider;
|
||||
}
|
||||
|
||||
orig_config = in;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// ModuleListModel
|
||||
//----------------------------------------------------------------------------
|
||||
class ModuleListModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QList<Pkcs11ProviderConfig> list;
|
||||
|
||||
ModuleListModel(QObject *parent = 0)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return list.count();
|
||||
}
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
if (index.row() >= list.count())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole)
|
||||
return list[index.row()].name;
|
||||
else if (role == Qt::EditRole)
|
||||
return list[index.row()].name;
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return Qt::ItemIsEnabled;
|
||||
|
||||
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (index.isValid() && role == Qt::EditRole) {
|
||||
QString str = value.toString();
|
||||
|
||||
if (str.isEmpty()) {
|
||||
emit editFailed(index, tr("Module name cannot be blank."));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool have_name_already = false;
|
||||
int at = index.row();
|
||||
for (int n = 0; n < list.count(); ++n) {
|
||||
const Pkcs11ProviderConfig &i = list[n];
|
||||
|
||||
// skip self
|
||||
if (n == at)
|
||||
continue;
|
||||
|
||||
if (i.name == str) {
|
||||
have_name_already = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (have_name_already) {
|
||||
emit editFailed(index, tr("There is already a module with this name."));
|
||||
return false;
|
||||
}
|
||||
|
||||
list[index.row()].name = str;
|
||||
emit dataChanged(index, index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void addItems(const QList<Pkcs11ProviderConfig> &items)
|
||||
{
|
||||
if (items.isEmpty())
|
||||
return;
|
||||
|
||||
beginInsertRows(QModelIndex(), list.size(), list.size() + items.count() - 1);
|
||||
list += items;
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void addItem(const Pkcs11ProviderConfig &i)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), list.size(), list.size());
|
||||
list += i;
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void removeItem(int at)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), at, at);
|
||||
list.removeAt(at);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void editFailed(const QModelIndex &index, const QString &reasonString);
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Pkcs11ConfigDlg
|
||||
//----------------------------------------------------------------------------
|
||||
static QCA::Provider *get_pkcs11_provider(QVariantMap *_config = 0)
|
||||
{
|
||||
QCA::ProviderList providers = QCA::providers();
|
||||
providers += QCA::defaultProvider();
|
||||
|
||||
QCA::Provider *provider = 0;
|
||||
QVariantMap config;
|
||||
foreach (QCA::Provider *p, providers) {
|
||||
config = QCA::getProviderConfig(p->name());
|
||||
if (!config.isEmpty() && config["formtype"] == "http://affinix.com/qca/forms/qca-pkcs11#1.0") {
|
||||
provider = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (provider && _config)
|
||||
*_config = config;
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
class Pkcs11ConfigDlg::Private : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Pkcs11ConfigDlg *q;
|
||||
Ui_Pkcs11Config ui;
|
||||
QString providerName;
|
||||
ModuleListModel *model;
|
||||
Pkcs11Config config;
|
||||
bool dirty;
|
||||
|
||||
// for safe dialog closing behavior during QListView editing
|
||||
bool allow_close;
|
||||
bool done;
|
||||
|
||||
// for ignoring modifications that we cause when populating fields
|
||||
bool ignore_dataChanged;
|
||||
|
||||
Private(Pkcs11ConfigDlg *_q, const QString &_providerName, const QVariantMap &configmap)
|
||||
: QObject(_q)
|
||||
, q(_q)
|
||||
, providerName(_providerName)
|
||||
, dirty(false)
|
||||
, allow_close(true)
|
||||
, done(false)
|
||||
, ignore_dataChanged(true)
|
||||
{
|
||||
ui.setupUi(q);
|
||||
q->resize(q->minimumSize());
|
||||
|
||||
model = new ModuleListModel(this);
|
||||
qRegisterMetaType<QModelIndex>("QModelIndex");
|
||||
// do this queued for two reasons:
|
||||
// 1) if we throw an error dialog, it will occur after the
|
||||
// row text has reverted, and the call stack completed
|
||||
// (the latter may not be required, but it helps me
|
||||
// sleep).
|
||||
// 2) if the user accepts/rejects the dialog while editing,
|
||||
// it is easy to ensure that the signal is not processed
|
||||
// (if it gets delivered at all).
|
||||
connect(model,
|
||||
SIGNAL(editFailed(const QModelIndex &, const QString &)),
|
||||
SLOT(model_editFailed(const QModelIndex &, const QString &)),
|
||||
Qt::QueuedConnection);
|
||||
|
||||
// set up widgets
|
||||
ui.rb_pincache_nolimit->setChecked(true);
|
||||
ui.sb_pincache_time->setEnabled(false);
|
||||
ui.sb_pincache_time->setValue(300);
|
||||
ui.lv_modules->setModel(model);
|
||||
ui.lv_modules->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked |
|
||||
QAbstractItemView::EditKeyPressed);
|
||||
ui.pb_remove->setEnabled(false);
|
||||
ui.tb_details->setEnabled(false);
|
||||
ui.gb_poll->setEnabled(false);
|
||||
ui.rb_polldefault->setChecked(true);
|
||||
ui.sb_pollcustom->setEnabled(false);
|
||||
ui.sb_pollcustom->setValue(5);
|
||||
ui.ck_modeauto->setChecked(true);
|
||||
|
||||
// disable this by default, enable on dataChanged
|
||||
ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
|
||||
// general
|
||||
connect(ui.ck_allowroot, SIGNAL(toggled(bool)), SLOT(dataChanged()));
|
||||
connect(ui.ck_allowprotected, SIGNAL(toggled(bool)), SLOT(dataChanged()));
|
||||
connect(ui.sb_loglevel, SIGNAL(valueChanged(int)), SLOT(dataChanged()));
|
||||
connect(ui.gb_pincache, SIGNAL(toggled(bool)), SLOT(dataChanged()));
|
||||
connect(ui.rb_pincache_nolimit, SIGNAL(toggled(bool)), SLOT(dataChanged()));
|
||||
connect(ui.rb_pincache_time, SIGNAL(toggled(bool)), ui.sb_pincache_time, SLOT(setEnabled(bool)));
|
||||
connect(ui.rb_pincache_time, SIGNAL(toggled(bool)), SLOT(dataChanged()));
|
||||
connect(ui.sb_pincache_time, SIGNAL(valueChanged(int)), SLOT(dataChanged()));
|
||||
|
||||
// modules
|
||||
connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), SLOT(dataChanged()));
|
||||
connect(ui.lv_modules->selectionModel(),
|
||||
SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
|
||||
SLOT(module_selectionChanged(const QItemSelection &, const QItemSelection &)));
|
||||
connect(ui.pb_add, SIGNAL(clicked()), SLOT(module_add()));
|
||||
connect(ui.pb_remove, SIGNAL(clicked()), SLOT(module_remove()));
|
||||
connect(ui.le_library, SIGNAL(textChanged(const QString &)), SLOT(dataChanged()));
|
||||
connect(ui.pb_browse, SIGNAL(clicked()), SLOT(library_browse()));
|
||||
connect(ui.cb_slotmethod, SIGNAL(currentIndexChanged(int)), SLOT(slotmethod_currentIndexChanged(int)));
|
||||
connect(ui.rb_polldefault, SIGNAL(toggled(bool)), SLOT(dataChanged()));
|
||||
connect(ui.rb_pollcustom, SIGNAL(toggled(bool)), ui.sb_pollcustom, SLOT(setEnabled(bool)));
|
||||
connect(ui.rb_pollcustom, SIGNAL(toggled(bool)), SLOT(dataChanged()));
|
||||
connect(ui.sb_pollcustom, SIGNAL(valueChanged(int)), SLOT(dataChanged()));
|
||||
connect(ui.ck_modallowprotected, SIGNAL(toggled(bool)), SLOT(dataChanged()));
|
||||
connect(ui.ck_certprivate, SIGNAL(toggled(bool)), SLOT(dataChanged()));
|
||||
connect(ui.ck_modeauto, SIGNAL(toggled(bool)), SLOT(modeauto_toggled(bool)));
|
||||
connect(ui.ck_modesign, SIGNAL(toggled(bool)), SLOT(modenonauto_toggled(bool)));
|
||||
connect(ui.ck_modesignrecover, SIGNAL(toggled(bool)), SLOT(modenonauto_toggled(bool)));
|
||||
connect(ui.ck_modedecrypt, SIGNAL(toggled(bool)), SLOT(modenonauto_toggled(bool)));
|
||||
connect(ui.ck_modeunwrap, SIGNAL(toggled(bool)), SLOT(modenonauto_toggled(bool)));
|
||||
|
||||
// is this a valid config?
|
||||
if (!providerName.isEmpty() && config.fromVariantMap(configmap)) {
|
||||
// if so, load everything up
|
||||
ui.ck_allowroot->setChecked(config.allow_load_rootca);
|
||||
ui.ck_allowprotected->setChecked(config.allow_protected_authentication);
|
||||
ui.sb_loglevel->setValue(config.log_level);
|
||||
if (config.pin_cache != 0) {
|
||||
ui.gb_pincache->setChecked(true);
|
||||
if (config.pin_cache <= -1)
|
||||
ui.rb_pincache_nolimit->setChecked(true);
|
||||
else {
|
||||
ui.rb_pincache_time->setChecked(true);
|
||||
ui.sb_pincache_time->setValue(config.pin_cache);
|
||||
}
|
||||
}
|
||||
|
||||
model->addItems(config.providers);
|
||||
if (!model->list.isEmpty()) {
|
||||
QModelIndex index = model->index(0);
|
||||
ui.lv_modules->setCurrentIndex(index);
|
||||
ui.lv_modules->selectionModel()->select(
|
||||
index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
|
||||
}
|
||||
ui.buttonBox->setFocus();
|
||||
ui.buttonBox->button(QDialogButtonBox::Cancel)->setFocus();
|
||||
} else {
|
||||
// otherwise, disable everything
|
||||
ui.gb_general->setEnabled(false);
|
||||
ui.gb_modules->setEnabled(false);
|
||||
ui.buttonBox->setFocus();
|
||||
ui.buttonBox->button(QDialogButtonBox::Cancel)->setFocus();
|
||||
}
|
||||
|
||||
ignore_dataChanged = false;
|
||||
}
|
||||
|
||||
void save_module(int at)
|
||||
{
|
||||
// save all options (except the name, which is handled by the model)
|
||||
Pkcs11ProviderConfig &i = model->list[at];
|
||||
|
||||
i.library = ui.le_library->text();
|
||||
i.enabled = true;
|
||||
|
||||
int x = ui.cb_slotmethod->currentIndex();
|
||||
if (x == 0)
|
||||
i.slotevent_method = "auto";
|
||||
else if (x == 1)
|
||||
i.slotevent_method = "trigger";
|
||||
else // 2
|
||||
i.slotevent_method = "poll";
|
||||
if (x == 2) {
|
||||
if (ui.rb_polldefault->isChecked())
|
||||
i.slotevent_timeout = 0;
|
||||
else
|
||||
i.slotevent_timeout = ui.sb_pollcustom->value();
|
||||
} else
|
||||
i.slotevent_timeout = 0;
|
||||
|
||||
i.allow_protected_authentication = ui.ck_modallowprotected->isChecked();
|
||||
i.cert_private = ui.ck_certprivate->isChecked();
|
||||
|
||||
i.private_mask = 0;
|
||||
if (ui.ck_modesign->isChecked())
|
||||
i.private_mask |= 1;
|
||||
if (ui.ck_modesignrecover->isChecked())
|
||||
i.private_mask |= 2;
|
||||
if (ui.ck_modedecrypt->isChecked())
|
||||
i.private_mask |= 4;
|
||||
if (ui.ck_modeunwrap->isChecked())
|
||||
i.private_mask |= 8;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
// save currently selected module, which may not be saved yet
|
||||
QItemSelection selection = ui.lv_modules->selectionModel()->selection();
|
||||
if (!selection.indexes().isEmpty()) {
|
||||
QModelIndex index = selection.indexes().first();
|
||||
save_module(index.row());
|
||||
}
|
||||
|
||||
config.allow_load_rootca = ui.ck_allowroot->isChecked();
|
||||
config.allow_protected_authentication = ui.ck_allowprotected->isChecked();
|
||||
config.log_level = ui.sb_loglevel->value();
|
||||
if (ui.gb_pincache->isChecked()) {
|
||||
if (ui.rb_pincache_nolimit->isChecked())
|
||||
config.pin_cache = -1;
|
||||
else
|
||||
config.pin_cache = ui.sb_pincache_time->value();
|
||||
} else
|
||||
config.pin_cache = 0;
|
||||
|
||||
config.providers = model->list;
|
||||
|
||||
QVariantMap configmap = config.toVariantMap();
|
||||
QCA::setProviderConfig(providerName, configmap);
|
||||
QCA::saveProviderConfig(providerName);
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void model_editFailed(const QModelIndex &index, const QString &reasonString)
|
||||
{
|
||||
// if the dialog has already been dismissed, then don't
|
||||
// bother with handling the editing failure
|
||||
if (done)
|
||||
return;
|
||||
|
||||
// show error dialog, and don't allow dimissing the dialog
|
||||
// during. we need this, because the dismiss request
|
||||
// can be queued, and end up being invoked during the
|
||||
// QMessageBox nested eventloop.
|
||||
allow_close = false;
|
||||
QMessageBox::information(q, tr("Module Configuration"), reasonString);
|
||||
allow_close = true;
|
||||
|
||||
// return to edit mode for the item
|
||||
ui.lv_modules->setFocus();
|
||||
ui.lv_modules->setCurrentIndex(index);
|
||||
ui.lv_modules->selectionModel()->select(
|
||||
index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
|
||||
ui.lv_modules->edit(index);
|
||||
}
|
||||
|
||||
void dataChanged()
|
||||
{
|
||||
if (ignore_dataChanged)
|
||||
return;
|
||||
|
||||
if (dirty)
|
||||
return;
|
||||
|
||||
dirty = true;
|
||||
ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
||||
}
|
||||
|
||||
void module_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
||||
{
|
||||
if (!deselected.indexes().isEmpty()) {
|
||||
QModelIndex index = deselected.indexes().first();
|
||||
save_module(index.row());
|
||||
}
|
||||
|
||||
ignore_dataChanged = true;
|
||||
|
||||
if (!selected.indexes().isEmpty()) {
|
||||
if (deselected.indexes().isEmpty()) {
|
||||
ui.pb_remove->setEnabled(true);
|
||||
ui.tb_details->setEnabled(true);
|
||||
}
|
||||
|
||||
QModelIndex index = selected.indexes().first();
|
||||
const Pkcs11ProviderConfig &i = model->list[index.row()];
|
||||
|
||||
ui.le_library->setText(i.library);
|
||||
|
||||
if (i.slotevent_method == "trigger")
|
||||
ui.cb_slotmethod->setCurrentIndex(1);
|
||||
else if (i.slotevent_method == "poll") {
|
||||
ui.cb_slotmethod->setCurrentIndex(2);
|
||||
if (i.slotevent_timeout <= 0)
|
||||
ui.rb_polldefault->setChecked(true);
|
||||
else {
|
||||
ui.rb_pollcustom->setChecked(true);
|
||||
ui.sb_pollcustom->setValue(i.slotevent_timeout);
|
||||
}
|
||||
} else // auto
|
||||
ui.cb_slotmethod->setCurrentIndex(0);
|
||||
if (i.slotevent_method != "poll") {
|
||||
ui.rb_polldefault->setChecked(true);
|
||||
ui.sb_pollcustom->setValue(5);
|
||||
}
|
||||
|
||||
ui.ck_modallowprotected->setChecked(i.allow_protected_authentication);
|
||||
ui.ck_certprivate->setChecked(i.cert_private);
|
||||
|
||||
if (i.private_mask == 0)
|
||||
ui.ck_modeauto->setChecked(true);
|
||||
else {
|
||||
ui.ck_modesign->setChecked(i.private_mask & 1);
|
||||
ui.ck_modesignrecover->setChecked(i.private_mask & 2);
|
||||
ui.ck_modedecrypt->setChecked(i.private_mask & 4);
|
||||
ui.ck_modeunwrap->setChecked(i.private_mask & 8);
|
||||
}
|
||||
} else if (selected.indexes().isEmpty() && !deselected.indexes().isEmpty()) {
|
||||
// restore defaults for all details widgets
|
||||
ui.le_library->setText(QString());
|
||||
ui.cb_slotmethod->setCurrentIndex(0);
|
||||
ui.rb_polldefault->setChecked(true);
|
||||
ui.sb_pollcustom->setValue(5);
|
||||
ui.ck_modallowprotected->setChecked(false);
|
||||
ui.ck_certprivate->setChecked(false);
|
||||
ui.ck_modeauto->setChecked(true);
|
||||
|
||||
// flip to first page, disable
|
||||
ui.tb_details->setCurrentIndex(0);
|
||||
ui.pb_remove->setEnabled(false);
|
||||
ui.tb_details->setEnabled(false);
|
||||
}
|
||||
|
||||
ignore_dataChanged = false;
|
||||
}
|
||||
|
||||
void module_add()
|
||||
{
|
||||
// find unused default name
|
||||
QString name;
|
||||
for (int n = 1;; ++n) {
|
||||
if (n == 1)
|
||||
name = tr("New Module");
|
||||
else
|
||||
name = tr("New Module (%1)").arg(n);
|
||||
|
||||
bool have_name_already = false;
|
||||
for (int n = 0; n < model->list.count(); ++n) {
|
||||
const Pkcs11ProviderConfig &i = model->list[n];
|
||||
if (i.name == name) {
|
||||
have_name_already = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!have_name_already)
|
||||
break;
|
||||
}
|
||||
|
||||
Pkcs11ProviderConfig i;
|
||||
i.name = name;
|
||||
i.enabled = true;
|
||||
model->addItem(i);
|
||||
|
||||
dataChanged();
|
||||
|
||||
QModelIndex index = model->index(model->list.count() - 1);
|
||||
|
||||
// flip to first page
|
||||
ui.tb_details->setCurrentIndex(0);
|
||||
|
||||
// edit this item
|
||||
ui.lv_modules->setFocus();
|
||||
ui.lv_modules->setCurrentIndex(index);
|
||||
ui.lv_modules->selectionModel()->select(
|
||||
index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current);
|
||||
ui.lv_modules->edit(index);
|
||||
}
|
||||
|
||||
void module_remove()
|
||||
{
|
||||
QItemSelection selection = ui.lv_modules->selectionModel()->selection();
|
||||
if (selection.indexes().isEmpty())
|
||||
return;
|
||||
QModelIndex index = selection.indexes().first();
|
||||
model->removeItem(index.row());
|
||||
|
||||
dataChanged();
|
||||
}
|
||||
|
||||
void library_browse()
|
||||
{
|
||||
QString fileName =
|
||||
QFileDialog::getOpenFileName(q, tr("Select PKCS#11 Module"), QString(), tr("PKCS#11 Modules (*.*)"));
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
|
||||
ui.le_library->setText(fileName);
|
||||
}
|
||||
|
||||
void slotmethod_currentIndexChanged(int index)
|
||||
{
|
||||
if (index == 2) // Polling
|
||||
ui.gb_poll->setEnabled(true);
|
||||
else
|
||||
ui.gb_poll->setEnabled(false);
|
||||
|
||||
dataChanged();
|
||||
}
|
||||
|
||||
void modeauto_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
if (ui.ck_modesign->isChecked())
|
||||
ui.ck_modesign->setChecked(false);
|
||||
if (ui.ck_modesignrecover->isChecked())
|
||||
ui.ck_modesignrecover->setChecked(false);
|
||||
if (ui.ck_modedecrypt->isChecked())
|
||||
ui.ck_modedecrypt->setChecked(false);
|
||||
if (ui.ck_modeunwrap->isChecked())
|
||||
ui.ck_modeunwrap->setChecked(false);
|
||||
} else {
|
||||
if (!ui.ck_modesign->isChecked() && !ui.ck_modesignrecover->isChecked() &&
|
||||
!ui.ck_modedecrypt->isChecked() && !ui.ck_modeunwrap->isChecked()) {
|
||||
ui.ck_modesign->setChecked(true);
|
||||
ui.ck_modesignrecover->setChecked(true);
|
||||
ui.ck_modedecrypt->setChecked(true);
|
||||
ui.ck_modeunwrap->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
dataChanged();
|
||||
}
|
||||
|
||||
void modenonauto_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
if (ui.ck_modeauto->isChecked())
|
||||
ui.ck_modeauto->setChecked(false);
|
||||
} else {
|
||||
if (!ui.ck_modesign->isChecked() && !ui.ck_modesignrecover->isChecked() &&
|
||||
!ui.ck_modedecrypt->isChecked() && !ui.ck_modeunwrap->isChecked()) {
|
||||
ui.ck_modeauto->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
dataChanged();
|
||||
}
|
||||
};
|
||||
|
||||
Pkcs11ConfigDlg::Pkcs11ConfigDlg(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
QVariantMap config;
|
||||
QCA::Provider *p = get_pkcs11_provider(&config);
|
||||
if (p)
|
||||
d = new Private(this, p->name(), config);
|
||||
else
|
||||
d = new Private(this, QString(), QVariantMap());
|
||||
}
|
||||
|
||||
Pkcs11ConfigDlg::Pkcs11ConfigDlg(const QString &providerName, const QVariantMap &config, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
d = new Private(this, providerName, config);
|
||||
}
|
||||
|
||||
Pkcs11ConfigDlg::~Pkcs11ConfigDlg()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void Pkcs11ConfigDlg::done(int r)
|
||||
{
|
||||
if (!d->allow_close)
|
||||
return;
|
||||
|
||||
d->done = true;
|
||||
if (r == Accepted)
|
||||
d->save();
|
||||
QDialog::done(r);
|
||||
}
|
||||
|
||||
bool Pkcs11ConfigDlg::isSupported()
|
||||
{
|
||||
return (get_pkcs11_provider() ? true : false);
|
||||
}
|
||||
|
||||
#include "pkcs11configdlg.moc"
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef PKCS11CONFIGDLG_H
|
||||
#define PKCS11CONFIGDLG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QVariantMap>
|
||||
|
||||
// support for the 'http://affinix.com/qca/forms/qca-pkcs11#1.0' provider form
|
||||
|
||||
class Pkcs11ConfigDlg : public QDialog
|
||||
{
|
||||
public:
|
||||
Pkcs11ConfigDlg(QWidget *parent = 0);
|
||||
Pkcs11ConfigDlg(const QString &providerName, const QVariantMap &config, QWidget *parent = 0);
|
||||
~Pkcs11ConfigDlg();
|
||||
|
||||
static bool isSupported();
|
||||
|
||||
protected slots:
|
||||
virtual void done(int r);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "prompter.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QtCore>
|
||||
#include <QtCrypto>
|
||||
#include <QtGui>
|
||||
|
||||
class Prompter::Private : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Prompter *q;
|
||||
|
||||
class Item
|
||||
{
|
||||
public:
|
||||
int id;
|
||||
QCA::Event event;
|
||||
};
|
||||
|
||||
QCA::EventHandler handler;
|
||||
QList<Item> pending;
|
||||
bool prompting;
|
||||
QMessageBox *token_prompt;
|
||||
bool auto_accept;
|
||||
|
||||
QCA::KeyStoreManager ksm;
|
||||
QList<QCA::KeyStore *> keyStores;
|
||||
|
||||
Private(Prompter *_q)
|
||||
: QObject(_q)
|
||||
, q(_q)
|
||||
, handler(this)
|
||||
, prompting(false)
|
||||
, token_prompt(0)
|
||||
, ksm(this)
|
||||
{
|
||||
connect(&handler, SIGNAL(eventReady(int, const QCA::Event &)), SLOT(ph_eventReady(int, const QCA::Event &)));
|
||||
handler.start();
|
||||
|
||||
connect(&ksm, SIGNAL(keyStoreAvailable(const QString &)), SLOT(ks_available(const QString &)));
|
||||
foreach (const QString &keyStoreId, ksm.keyStores())
|
||||
ks_available(keyStoreId);
|
||||
}
|
||||
|
||||
~Private()
|
||||
{
|
||||
qDeleteAll(keyStores);
|
||||
|
||||
while (!pending.isEmpty())
|
||||
handler.reject(pending.takeFirst().id);
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void ph_eventReady(int id, const QCA::Event &event)
|
||||
{
|
||||
Item i;
|
||||
i.id = id;
|
||||
i.event = event;
|
||||
pending += i;
|
||||
nextEvent();
|
||||
}
|
||||
|
||||
void nextEvent()
|
||||
{
|
||||
if (prompting || pending.isEmpty())
|
||||
return;
|
||||
|
||||
prompting = true;
|
||||
|
||||
const Item &i = pending.first();
|
||||
const int &id = i.id;
|
||||
const QCA::Event &event = i.event;
|
||||
|
||||
if (event.type() == QCA::Event::Password) {
|
||||
QCA::SecureArray known = q->knownPassword(event);
|
||||
if (!known.isEmpty()) {
|
||||
handler.submitPassword(id, known);
|
||||
goto end;
|
||||
}
|
||||
|
||||
QString type = Prompter::tr("password");
|
||||
if (event.passwordStyle() == QCA::Event::StylePassphrase)
|
||||
type = Prompter::tr("passphrase");
|
||||
else if (event.passwordStyle() == QCA::Event::StylePIN)
|
||||
type = Prompter::tr("PIN");
|
||||
|
||||
QString str;
|
||||
if (event.source() == QCA::Event::KeyStore) {
|
||||
QString name;
|
||||
QCA::KeyStoreEntry entry = event.keyStoreEntry();
|
||||
if (!entry.isNull()) {
|
||||
name = entry.name();
|
||||
} else {
|
||||
if (event.keyStoreInfo().type() == QCA::KeyStore::SmartCard)
|
||||
name = Prompter::tr("the '%1' token").arg(event.keyStoreInfo().name());
|
||||
else
|
||||
name = event.keyStoreInfo().name();
|
||||
}
|
||||
str = Prompter::tr("Enter %1 for %2").arg(type, name);
|
||||
} else if (!event.fileName().isEmpty()) {
|
||||
QFileInfo fi(event.fileName());
|
||||
str = Prompter::tr("Enter %1 for %2:").arg(type, fi.fileName());
|
||||
} else
|
||||
str = Prompter::tr("Enter %1:").arg(type);
|
||||
|
||||
bool ok;
|
||||
QString pass = QInputDialog::getText(0,
|
||||
QApplication::instance()->applicationName() + ": " + tr("Prompt"),
|
||||
str,
|
||||
QLineEdit::Password,
|
||||
QString(),
|
||||
&ok);
|
||||
if (ok) {
|
||||
QCA::SecureArray password = pass.toUtf8();
|
||||
q->userSubmitted(password, event);
|
||||
handler.submitPassword(id, password);
|
||||
} else
|
||||
handler.reject(id);
|
||||
} else if (event.type() == QCA::Event::Token) {
|
||||
// even though we're being prompted for a missing token,
|
||||
// we should still check if the token is present, due to
|
||||
// a possible race between insert and token request.
|
||||
bool found = false;
|
||||
|
||||
// token-only
|
||||
if (event.keyStoreEntry().isNull()) {
|
||||
foreach (QCA::KeyStore *ks, keyStores) {
|
||||
if (ks->id() == event.keyStoreInfo().id()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// token-entry
|
||||
else {
|
||||
QCA::KeyStoreEntry kse = event.keyStoreEntry();
|
||||
|
||||
QCA::KeyStore *ks = 0;
|
||||
foreach (QCA::KeyStore *i, keyStores) {
|
||||
if (i->id() == event.keyStoreInfo().id()) {
|
||||
ks = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ks) {
|
||||
QList<QCA::KeyStoreEntry> list = ks->entryList();
|
||||
foreach (const QCA::KeyStoreEntry &e, list) {
|
||||
if (e.id() == kse.id() && kse.isAvailable()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
// auto-accept
|
||||
handler.tokenOkay(id);
|
||||
return;
|
||||
}
|
||||
|
||||
QCA::KeyStoreEntry entry = event.keyStoreEntry();
|
||||
QString name;
|
||||
if (!entry.isNull()) {
|
||||
name = Prompter::tr("Please make %1 (of %2) available").arg(entry.name(), entry.storeName());
|
||||
} else {
|
||||
name = Prompter::tr("Please insert the '%1' token").arg(event.keyStoreInfo().name());
|
||||
}
|
||||
|
||||
QString str = Prompter::tr("%1 and click OK.").arg(name);
|
||||
|
||||
QMessageBox msgBox(QMessageBox::Information,
|
||||
QApplication::instance()->applicationName() + ": " + tr("Prompt"),
|
||||
str,
|
||||
QMessageBox::Ok | QMessageBox::Cancel,
|
||||
0);
|
||||
token_prompt = &msgBox;
|
||||
auto_accept = false;
|
||||
if (msgBox.exec() == QMessageBox::Ok || auto_accept)
|
||||
handler.tokenOkay(id);
|
||||
else
|
||||
handler.reject(id);
|
||||
token_prompt = 0;
|
||||
} else
|
||||
handler.reject(id);
|
||||
|
||||
end:
|
||||
pending.removeFirst();
|
||||
prompting = false;
|
||||
|
||||
if (!pending.isEmpty())
|
||||
QMetaObject::invokeMethod(this, "nextEvent", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void ks_available(const QString &keyStoreId)
|
||||
{
|
||||
QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, &ksm);
|
||||
connect(ks, SIGNAL(updated()), SLOT(ks_updated()));
|
||||
connect(ks, SIGNAL(unavailable()), SLOT(ks_unavailable()));
|
||||
keyStores += ks;
|
||||
ks->startAsynchronousMode();
|
||||
|
||||
// are we currently in a token-only prompt?
|
||||
if (token_prompt && pending.first().event.type() == QCA::Event::Token &&
|
||||
pending.first().event.keyStoreEntry().isNull()) {
|
||||
// was the token we're looking for just inserted?
|
||||
if (pending.first().event.keyStoreInfo().id() == keyStoreId) {
|
||||
// auto-accept
|
||||
auto_accept = true;
|
||||
token_prompt->accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ks_unavailable()
|
||||
{
|
||||
QCA::KeyStore *ks = (QCA::KeyStore *)sender();
|
||||
keyStores.removeAll(ks);
|
||||
delete ks;
|
||||
}
|
||||
|
||||
void ks_updated()
|
||||
{
|
||||
QCA::KeyStore *ks = (QCA::KeyStore *)sender();
|
||||
|
||||
// are we currently in a token-entry prompt?
|
||||
if (token_prompt && pending.first().event.type() == QCA::Event::Token &&
|
||||
!pending.first().event.keyStoreEntry().isNull()) {
|
||||
QCA::KeyStoreEntry kse = pending.first().event.keyStoreEntry();
|
||||
|
||||
// was the token of the entry we're looking for updated?
|
||||
if (pending.first().event.keyStoreInfo().id() == ks->id()) {
|
||||
// is the entry available?
|
||||
bool avail = false;
|
||||
QList<QCA::KeyStoreEntry> list = ks->entryList();
|
||||
foreach (const QCA::KeyStoreEntry &e, list) {
|
||||
if (e.id() == kse.id()) {
|
||||
avail = kse.isAvailable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (avail) {
|
||||
// auto-accept
|
||||
auto_accept = true;
|
||||
token_prompt->accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Prompter::Prompter(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
d = new Private(this);
|
||||
}
|
||||
|
||||
Prompter::~Prompter()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
QCA::SecureArray Prompter::knownPassword(const QCA::Event &event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
return QCA::SecureArray();
|
||||
}
|
||||
|
||||
void Prompter::userSubmitted(const QCA::SecureArray &password, const QCA::Event &event)
|
||||
{
|
||||
Q_UNUSED(password);
|
||||
Q_UNUSED(event);
|
||||
}
|
||||
|
||||
#include "prompter.moc"
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef PROMPTER_H
|
||||
#define PROMPTER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace QCA {
|
||||
class SecureArray;
|
||||
class Event;
|
||||
}
|
||||
|
||||
class Prompter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Prompter(QObject *parent = nullptr);
|
||||
~Prompter();
|
||||
|
||||
protected:
|
||||
// called with every password event, to check for a known value.
|
||||
// reimplement it to provide known/cached passwords.
|
||||
virtual QCA::SecureArray knownPassword(const QCA::Event &event);
|
||||
|
||||
// called when a user-entered password is submitted. note that this
|
||||
// does not mean the password was correct. to know if the password
|
||||
// was correct, you'll have to match up the event information with
|
||||
// the operation that triggered it.
|
||||
virtual void userSubmitted(const QCA::SecureArray &password, const QCA::Event &event);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
friend class Private;
|
||||
Private *d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
set(eventhandlerdemo_bin_SRCS eventhandlerdemo.cpp)
|
||||
|
||||
add_executable(eventhandlerdemo ${eventhandlerdemo_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(eventhandlerdemo)
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright (C) 2007 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// QtCrypto has the declarations for all of QCA
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
We need a class on the client side to handle password requests.
|
||||
*/
|
||||
class ClientPassphraseHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ClientPassphraseHandler(QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
{
|
||||
// When the PasswordAsker or TokenAsker needs to interact
|
||||
// with the user, it raises a signal. We connect that to a
|
||||
// local slot to get the required information.
|
||||
connect(&m_handler, &QCA::EventHandler::eventReady, this, &ClientPassphraseHandler::my_eventReady);
|
||||
|
||||
// Now that we are set up, we can start the EventHandler. Nothing
|
||||
// will happen if you don't call this method.
|
||||
m_handler.start();
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
// This slot gets called when the provider needs a token inserted,
|
||||
// or to get a passphrase / password / PIN.
|
||||
void my_eventReady(int id, const QCA::Event &event)
|
||||
{
|
||||
// We can sanity check the event
|
||||
if (event.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Events can be associated with a a keystore or a file/bytearray
|
||||
// You can tell which by looking at the Source
|
||||
if (event.source() == QCA::Event::KeyStore) {
|
||||
std::cout << "Event is associated with a key store operation" << std::endl;
|
||||
} else if (event.source() == QCA::Event::Data) {
|
||||
std::cout << "Event is associated with a file or some other data" << std::endl;
|
||||
// if the event comes from a file type operation, you can get the
|
||||
// name / label using fileName()
|
||||
std::cout << " Filename: " << qPrintable(event.fileName()) << std::endl;
|
||||
} else {
|
||||
std::cout << "Unexpected Source for Event" << std::endl;
|
||||
}
|
||||
|
||||
// There are different kinds of events.
|
||||
if (event.type() == QCA::Event::Token) {
|
||||
// You would typically ask the user to insert the token here
|
||||
std::cout << "Request for token" << std::endl;
|
||||
// we just fake it for this demo.
|
||||
m_handler.tokenOkay(id);
|
||||
// you could use m_handler.reject( id ) to refuse the token request
|
||||
|
||||
} else if (event.type() == QCA::Event::Password) {
|
||||
std::cout << "Request for password, passphrase or PIN" << std::endl;
|
||||
// and within the Password type, we have a few different styles.
|
||||
if (event.passwordStyle() == QCA::Event::StylePassword) {
|
||||
std::cout << " [Password request]" << std::endl;
|
||||
} else if (event.passwordStyle() == QCA::Event::StylePassphrase) {
|
||||
std::cout << " [Passphrase request]" << std::endl;
|
||||
} else if (event.passwordStyle() == QCA::Event::StylePIN) {
|
||||
std::cout << " [PIN request]" << std::endl;
|
||||
} else {
|
||||
std::cout << " [unexpect request style]" << std::endl;
|
||||
}
|
||||
// You would typically request the password/PIN/passphrase.
|
||||
// again, we just fake it.
|
||||
m_handler.submitPassword(id, QCA::SecureArray("hello"));
|
||||
|
||||
} else {
|
||||
std::cout << "Unexpected event type" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QCA::EventHandler m_handler;
|
||||
};
|
||||
|
||||
void asker_procedure();
|
||||
|
||||
class AskerThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
void run() override
|
||||
{
|
||||
asker_procedure();
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication exampleApp(argc, argv);
|
||||
|
||||
ClientPassphraseHandler cph;
|
||||
|
||||
// handler and asker cannot occur in the same thread
|
||||
AskerThread askerThread;
|
||||
QObject::connect(&askerThread, &AskerThread::finished, &exampleApp, &QCoreApplication::quit);
|
||||
askerThread.start();
|
||||
|
||||
exampleApp.exec();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void asker_procedure()
|
||||
{
|
||||
QCA::PasswordAsker pwAsker;
|
||||
|
||||
pwAsker.ask(QCA::Event::StylePassword, QStringLiteral("foo.tmp"), nullptr);
|
||||
|
||||
pwAsker.waitForResponse();
|
||||
|
||||
std::cout << "Password was: " << pwAsker.password().toByteArray().data() << std::endl;
|
||||
|
||||
std::cout << std::endl << "Now do token:" << std::endl;
|
||||
|
||||
QCA::TokenAsker tokenAsker;
|
||||
|
||||
tokenAsker.ask(
|
||||
QCA::KeyStoreInfo(QCA::KeyStore::SmartCard, QStringLiteral("Token Id"), QStringLiteral("Token Name")),
|
||||
QCA::KeyStoreEntry(),
|
||||
nullptr);
|
||||
|
||||
tokenAsker.waitForResponse();
|
||||
|
||||
if (tokenAsker.accepted()) {
|
||||
std::cout << "Token was accepted" << std::endl;
|
||||
} else {
|
||||
std::cout << "Token was not accepted" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#include "eventhandlerdemo.moc"
|
||||
@@ -0,0 +1,182 @@
|
||||
/** \example aes-cmac.cpp
|
||||
|
||||
This example shows how to implement a client side "provider".
|
||||
|
||||
There are three important parts to this:
|
||||
- the class derived from QCA::Provider (in this example called
|
||||
"ClientSideProvider"), that generates the context class
|
||||
- one or more context classes (in this example only one, implementing
|
||||
AES-CMAC, called "AESCMACContext")
|
||||
- a call to QCA::insertProvider, to add the QCA::Provider subclass
|
||||
into QCA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example base64test.cpp
|
||||
|
||||
The code below shows some simple operations on a QCA::Base64 object, converting
|
||||
between QCA::SecureArray and QString.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example certtest.cpp
|
||||
|
||||
This example shows how QCA::Certificate and QCA::CertificateCollection
|
||||
can be used. Note that the argument, if you provide it, must be a
|
||||
PEM encoded file collection.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example ciphertest.cpp
|
||||
|
||||
The code below shows the normal way to use the QCA::Cipher class.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/* \example cmssigner/main.cpp
|
||||
|
||||
The code below shows how to use Cryptographic Message Syntax (CMS)
|
||||
in a GUI application.
|
||||
|
||||
\include certitem.h
|
||||
\include certitem.cpp
|
||||
\include certviewdlg.h
|
||||
\include certviewdlg.cpp
|
||||
\include keyselectdlg.h
|
||||
\include keyselectdlg.cpp
|
||||
\include prompter.h
|
||||
\include prompter.cpp
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example eventhandlerdemo.cpp
|
||||
|
||||
The code below shows to implement a client side handler for
|
||||
password / passphrase / PIN and token requests from QCA and any
|
||||
associated providers.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example hashtest.cpp
|
||||
|
||||
The code below shows how to use the QCA::Hash class
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example hextest.cpp
|
||||
|
||||
The code below shows some simple operations on a QCA::Hex object, converting
|
||||
between QCA::SecureArray and QString.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example keyloader.cpp
|
||||
|
||||
The code below shows how to load a private key from a PEM format file,
|
||||
including handling any requirement for a passphrase. This is done using
|
||||
the QCA::KeyLoader class.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example mactest.cpp
|
||||
|
||||
The code below shows how to use the QCA::MessageAuthenticationCode class
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example md5crypt.cpp
|
||||
|
||||
The code below shows how to calculate an md5crypt based password. This
|
||||
code is compatible with the glibc code.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example providertest.cpp
|
||||
|
||||
The code below shows some simple operations on a QCA::Provider object, including
|
||||
use of iterators and some member functions.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example publickeyexample.cpp
|
||||
|
||||
The code below shows how to do public key encryption, decryption,
|
||||
signing and verification.
|
||||
*/
|
||||
|
||||
|
||||
/** \example randomtest.cpp
|
||||
|
||||
The code below shows the normal way to use the QCA::Random class.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example rsatest.cpp
|
||||
|
||||
The code below shows some of the capabilities for how to use
|
||||
RSA. This example also shows how to export and import a key
|
||||
to a file, using PEM encoding.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example saslserver.cpp
|
||||
|
||||
The code below shows how to create a SASL server.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example saslclient.cpp
|
||||
|
||||
The code below shows how to create a SASL client.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example sslservtest.cpp
|
||||
|
||||
The code below shows how to create an SSL server.
|
||||
|
||||
Note that this server returns a self-signed certificate
|
||||
for "example.com", and that the certificate is expired.
|
||||
|
||||
The design used here only allows for one connection
|
||||
at a time. If you want to allow for more, you should
|
||||
probably create a "TlsConnection" object that agregates
|
||||
a QCA::TLS object and a QTcpSocket (plus a little bit of
|
||||
state information) that handles a single connection. Then
|
||||
just create a TlsConnection for each server connection.
|
||||
|
||||
*/
|
||||
|
||||
/** \example ssltest.cpp
|
||||
|
||||
The code below shows how to create an SSL client
|
||||
|
||||
*/
|
||||
|
||||
/** \example tlssocket.cpp
|
||||
|
||||
The code below shows how to create a socket that can operate
|
||||
over an Transport Layer Security (TLS, also known as SSL) connection.
|
||||
|
||||
\include tlssocket/tlssocket.h
|
||||
\include tlssocket/main.cpp
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
set(hashtest_bin_SRCS hashtest.cpp)
|
||||
|
||||
add_executable(hashtest ${hashtest_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(hashtest)
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright (C) 2004 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// QtCrypto/QtCrypto has the declarations for all of QCA
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
// we use the first argument if provided, or
|
||||
// use "hello" if no arguments
|
||||
QCA::SecureArray arg = (argc >= 2) ? argv[1] : "hello";
|
||||
|
||||
// must always check that an algorithm is supported before using it
|
||||
if (!QCA::isSupported("sha1"))
|
||||
printf("SHA1 not supported!\n");
|
||||
else {
|
||||
// this shows the "all in one" approach
|
||||
QString result = QCA::Hash(QStringLiteral("sha1")).hashToString(arg);
|
||||
printf("sha1(\"%s\") = [%s]\n", arg.data(), qPrintable(result));
|
||||
}
|
||||
|
||||
// must always check that an algorithm is supported before using it
|
||||
if (!QCA::isSupported("md5"))
|
||||
printf("MD5 not supported!\n");
|
||||
else {
|
||||
// this shows the incremental approach. Naturally
|
||||
// for this simple job, we could use the "all in one"
|
||||
// approach - this is an example, after all :-)
|
||||
QCA::SecureArray part1(arg.toByteArray().left(3)); // three chars - "hel"
|
||||
QCA::SecureArray part2(arg.toByteArray().mid(3)); // the rest - "lo"
|
||||
|
||||
// create the required object.
|
||||
QCA::Hash hashObject(QStringLiteral("md5"));
|
||||
// we split it into two parts to show incremental update
|
||||
hashObject.update(part1);
|
||||
hashObject.update(part2);
|
||||
// no more updates after calling final.
|
||||
QCA::SecureArray resultArray = hashObject.final();
|
||||
// convert the result into printable hexadecimal.
|
||||
QString result = QCA::arrayToHex(resultArray.toByteArray());
|
||||
printf("md5(\"%s\") = [%s]\n", arg.data(), qPrintable(result));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
set(hextest_bin_SRCS hextest.cpp)
|
||||
|
||||
add_executable(hextest ${hextest_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(hextest)
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright (C) 2005 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// QtCrypto has the declarations for all of QCA
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
// we use the first argument as the data to encode / decode
|
||||
// if an argument is provided. Use "hello" if no argument
|
||||
QByteArray arg; // empty array
|
||||
arg.append((argc >= 2) ? argv[1] : "hello");
|
||||
|
||||
// create our object, which does encoding by default
|
||||
// QCA::Hex encoder(QCA::Encode); is equivalent
|
||||
QCA::Hex encoder;
|
||||
|
||||
// You might prefer to use encoder.encode(); and have
|
||||
// it return a QCA::SecureArray, depending on your needs
|
||||
QString encoded = encoder.arrayToString(arg);
|
||||
|
||||
std::cout << arg.data() << " in hex encoding is ";
|
||||
std::cout << encoded.toLatin1().data() << std::endl;
|
||||
|
||||
// This time, we'll create an object to decode hexadecimal.
|
||||
// We could also have reused the existing object, calling
|
||||
// clear(); and setup(QCA::Decode); on it.
|
||||
QCA::Hex decoder(QCA::Decode);
|
||||
|
||||
// This time, we convert a QString into a QString
|
||||
QString decoded = decoder.decodeString(encoded);
|
||||
|
||||
std::cout << encoded.toLatin1().data() << " decoded from hex is ";
|
||||
std::cout << decoded.toLatin1().data() << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
set(keyloader_bin_SRCS keyloader.cpp)
|
||||
|
||||
add_executable(keyloader ${keyloader_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(keyloader)
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// QtCrypto has the declarations for all of QCA
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
#include <QTimer>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
class PassphraseHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QCA::EventHandler handler;
|
||||
|
||||
PassphraseHandler(QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
{
|
||||
connect(&handler, &QCA::EventHandler::eventReady, this, &PassphraseHandler::eh_eventReady);
|
||||
handler.start();
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void eh_eventReady(int id, const QCA::Event &event)
|
||||
{
|
||||
if (event.type() == QCA::Event::Password) {
|
||||
QCA::SecureArray pass;
|
||||
QCA::ConsolePrompt prompt;
|
||||
prompt.getHidden(QStringLiteral("Passphrase"));
|
||||
prompt.waitForFinished();
|
||||
pass = prompt.result();
|
||||
handler.submitPassword(id, pass);
|
||||
} else
|
||||
handler.reject(id);
|
||||
}
|
||||
};
|
||||
|
||||
class App : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QCA::KeyLoader keyLoader;
|
||||
QString str;
|
||||
|
||||
App()
|
||||
{
|
||||
connect(&keyLoader, &QCA::KeyLoader::finished, this, &App::kl_finished);
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
void start()
|
||||
{
|
||||
keyLoader.loadPrivateKeyFromPEMFile(str);
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void quit();
|
||||
|
||||
private Q_SLOTS:
|
||||
void kl_finished()
|
||||
{
|
||||
if (keyLoader.convertResult() == QCA::ConvertGood) {
|
||||
QCA::PrivateKey key = keyLoader.privateKey();
|
||||
printf("Loaded successfully. Bits: %d\n", key.bitSize());
|
||||
} else
|
||||
printf("Unable to load.\n");
|
||||
|
||||
emit quit();
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCA::Initializer init;
|
||||
QCoreApplication qapp(argc, argv);
|
||||
|
||||
if (argc < 2) {
|
||||
printf("usage: keyloader [privatekey.pem]\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
PassphraseHandler passphraseHandler;
|
||||
App app;
|
||||
app.str = QFile::decodeName(argv[1]);
|
||||
QObject::connect(&app, &App::quit, &qapp, QCoreApplication::quit);
|
||||
QTimer::singleShot(0, &app, &App::start);
|
||||
qapp.exec();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "keyloader.moc"
|
||||
@@ -0,0 +1,5 @@
|
||||
set(mactest_bin_SRCS mactest.cpp)
|
||||
|
||||
add_executable(mactest ${mactest_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(mactest)
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright (C) 2004, 2006 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// QtCrypto has the declarations for all of QCA
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
|
||||
// needed for printf
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
qDebug() << "This example shows hashed MAC";
|
||||
|
||||
// we use the first argument as the data to authenticate
|
||||
// if an argument is provided. Use "hello" if no argument
|
||||
QByteArray arg = (argc >= 2) ? argv[1] : "hello";
|
||||
|
||||
// we use the second argument as the key to authenticate
|
||||
// with, if two arguments are provided. Use "secret" as
|
||||
// the key if less than two arguments.
|
||||
QCA::SecureArray key((argc >= 3) ? argv[2] : "secret");
|
||||
|
||||
// must always check that an algorithm is supported before using it
|
||||
if (!QCA::isSupported("hmac(sha1)")) {
|
||||
printf("HMAC(SHA1) not supported!\n");
|
||||
} else {
|
||||
// create the required object using HMAC with SHA-1, and an
|
||||
// empty key.
|
||||
QCA::MessageAuthenticationCode hmacObject(QStringLiteral("hmac(sha1)"), QCA::SecureArray());
|
||||
|
||||
// create the key
|
||||
QCA::SymmetricKey keyObject(key);
|
||||
|
||||
// set the HMAC object to use the key
|
||||
hmacObject.setup(key);
|
||||
// that could also have been done in the
|
||||
// QCA::MessageAuthenticationCode constructor
|
||||
|
||||
// we split it into two parts to show incremental update
|
||||
QCA::SecureArray part1(arg.left(3)); // three chars - "hel"
|
||||
QCA::SecureArray part2(arg.mid(3)); // the rest - "lo"
|
||||
hmacObject.update(part1);
|
||||
hmacObject.update(part2);
|
||||
|
||||
// no more updates after calling final.
|
||||
QCA::SecureArray resultArray = hmacObject.final();
|
||||
|
||||
// convert the result into printable hexadecimal.
|
||||
QString result = QCA::arrayToHex(resultArray.toByteArray());
|
||||
printf("HMAC(SHA1) of \"%s\" with \"%s\" = [%s]\n", arg.data(), key.data(), result.toLatin1().data());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
set(md5crypt_bin_SRCS md5crypt.cpp)
|
||||
|
||||
add_executable(md5crypt ${md5crypt_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(md5crypt)
|
||||
|
||||
#add "crypt" to the libs if you are trying the crypt() equivalent
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
Copyright (C) 2007 Carlo Todeschini - Metarete s.r.l. <info@metarete.it>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
Algorithm inspired by Vladimir Silva's "Secure Java apps on Linux using
|
||||
MD5 crypt" article
|
||||
(http://www-128.ibm.com/developerworks/linux/library/l-md5crypt/)
|
||||
*/
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QtCrypto>
|
||||
#include <QtDebug>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
QString to64(long v, int size)
|
||||
{
|
||||
// Character set of the encrypted password: A-Za-z0-9./
|
||||
QString itoa64 = QStringLiteral("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
|
||||
QString result;
|
||||
|
||||
while (--size >= 0) {
|
||||
result.append(itoa64.at((int)(v & 0x3f)));
|
||||
v = v >> 6;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int byte2unsigned(int byteValue)
|
||||
{
|
||||
int integerToReturn;
|
||||
integerToReturn = (int)byteValue & 0xff;
|
||||
return integerToReturn;
|
||||
}
|
||||
|
||||
QString qca_md5crypt(const QCA::SecureArray &password, const QCA::SecureArray &salt)
|
||||
{
|
||||
QCA::SecureArray finalState, magic_string = "$1$";
|
||||
|
||||
// The md5crypt algorithm uses two separate hashes
|
||||
QCA::Hash hash1(QStringLiteral("md5"));
|
||||
QCA::Hash hash2(QStringLiteral("md5"));
|
||||
|
||||
// MD5 Hash #1: pwd, magic string and salt
|
||||
hash1.update(password);
|
||||
hash1.update(magic_string);
|
||||
hash1.update(salt);
|
||||
|
||||
// MD5 Hash #2: password, salt, password
|
||||
hash2.update(password);
|
||||
hash2.update(salt);
|
||||
hash2.update(password);
|
||||
|
||||
finalState = hash2.final();
|
||||
|
||||
// Two sets of transformations based on the length of the password
|
||||
for (int i = password.size(); i > 0; i -= 16) {
|
||||
// Update hash1 from offset value (i > 16 ? 16 : i)
|
||||
hash1.update(finalState.toByteArray().left(i > 16 ? 16 : i));
|
||||
}
|
||||
|
||||
// Clear array bits
|
||||
finalState.fill(0);
|
||||
|
||||
for (int i = password.size(); i != 0; i = i >> 1) {
|
||||
if ((i & 1) != 0) {
|
||||
hash1.update(finalState.toByteArray().left(1));
|
||||
} else {
|
||||
hash1.update(password.toByteArray().left(1));
|
||||
}
|
||||
}
|
||||
|
||||
finalState = hash1.final();
|
||||
|
||||
// Now build a 1000 entry dictionary...
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
hash2.clear();
|
||||
|
||||
if ((i & 1) != 0) {
|
||||
hash2.update(password);
|
||||
} else {
|
||||
hash2.update(finalState.toByteArray().left(16));
|
||||
}
|
||||
|
||||
if ((i % 3) != 0) {
|
||||
hash2.update(salt);
|
||||
}
|
||||
|
||||
if ((i % 7) != 0) {
|
||||
hash2.update(password);
|
||||
}
|
||||
|
||||
if ((i & 1) != 0) {
|
||||
hash2.update(finalState.toByteArray().left(16));
|
||||
} else {
|
||||
hash2.update(password);
|
||||
}
|
||||
|
||||
finalState = hash2.final();
|
||||
}
|
||||
|
||||
// Create an output string
|
||||
// Salt is part of the encoded password ($1$<string>$)
|
||||
QString encodedString;
|
||||
|
||||
encodedString.append(QString::fromLatin1(magic_string.toByteArray()));
|
||||
encodedString.append(QString::fromLatin1(salt.toByteArray()));
|
||||
encodedString.append(QStringLiteral("$"));
|
||||
|
||||
long l;
|
||||
|
||||
l = (byte2unsigned(finalState.toByteArray().at(0)) << 16 | (byte2unsigned(finalState.toByteArray().at(6))) << 8 |
|
||||
byte2unsigned(finalState.toByteArray().at(12)));
|
||||
encodedString.append(to64(l, 4));
|
||||
|
||||
l = (byte2unsigned(finalState.toByteArray().at(1)) << 16 | (byte2unsigned(finalState.toByteArray().at(7))) << 8 |
|
||||
byte2unsigned(finalState.toByteArray().at(13)));
|
||||
encodedString.append(to64(l, 4));
|
||||
|
||||
l = (byte2unsigned(finalState.toByteArray().at(2)) << 16 | (byte2unsigned(finalState.toByteArray().at(8))) << 8 |
|
||||
byte2unsigned(finalState.toByteArray().at(14)));
|
||||
encodedString.append(to64(l, 4));
|
||||
|
||||
l = (byte2unsigned(finalState.toByteArray().at(3)) << 16 | (byte2unsigned(finalState.toByteArray().at(9))) << 8 |
|
||||
byte2unsigned(finalState.toByteArray().at(15)));
|
||||
encodedString.append(to64(l, 4));
|
||||
|
||||
l = (byte2unsigned(finalState.toByteArray().at(4)) << 16 | (byte2unsigned(finalState.toByteArray().at(10))) << 8 |
|
||||
byte2unsigned(finalState.toByteArray().at(5)));
|
||||
encodedString.append(to64(l, 4));
|
||||
|
||||
l = byte2unsigned(finalState.toByteArray().at(11));
|
||||
encodedString.append(to64(l, 2));
|
||||
|
||||
return encodedString;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
QCA::SecureArray password, salt;
|
||||
|
||||
if (argc < 3) {
|
||||
printf("Usage: %s password salt (salt without $1$)\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
password.append(argv[1]);
|
||||
|
||||
salt.append(argv[2]);
|
||||
|
||||
// must always check that an algorithm is supported before using it
|
||||
if (!QCA::isSupported("md5"))
|
||||
printf("MD5 hash not supported!\n");
|
||||
else {
|
||||
QString result = qca_md5crypt(password, salt);
|
||||
|
||||
printf("md5crypt [ %s , %s ] = '%s'\n", password.data(), salt.data(), qPrintable(result));
|
||||
|
||||
// this is equivalent if you have GNU libc 2.0
|
||||
// printf( "GNU md5crypt [ %s , %s ] = '%s'\n", password.data(), salt.data(), crypt( password.data(), (
|
||||
// "$1$"+salt ).data() ) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
set(providertest_bin_SRCS providertest.cpp)
|
||||
|
||||
add_executable(providertest ${providertest_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(providertest)
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright (C) 2004 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// QtCrypto has the declarations for all of QCA
|
||||
#include <QCoreApplication>
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <iostream>
|
||||
#include <qstringlist.h>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
// get all the available providers loaded.
|
||||
// you don't normally need this (because you test using isSupported())
|
||||
// but this is a special case.
|
||||
QCA::scanForPlugins();
|
||||
|
||||
// this gives us all the plugin providers as a list
|
||||
const QCA::ProviderList qcaProviders = QCA::providers();
|
||||
for (const QCA::Provider *provider : qcaProviders) {
|
||||
// each provider has a name, which we can display
|
||||
std::cout << provider->name().toLatin1().data() << ": ";
|
||||
// ... and also a list of features
|
||||
QStringList capabilities = provider->features();
|
||||
// we turn the string list back into a single string,
|
||||
// and display it as well
|
||||
std::cout << capabilities.join(QStringLiteral(", ")).toLatin1().data() << std::endl;
|
||||
}
|
||||
|
||||
// Note that the default provider isn't included in
|
||||
// the result of QCA::providers()
|
||||
std::cout << "default: ";
|
||||
// However it is still possible to get the features
|
||||
// supported by the default provider
|
||||
QStringList capabilities = QCA::defaultFeatures();
|
||||
std::cout << capabilities.join(QStringLiteral(", ")).toLatin1().data() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
set(publickeyexample_bin_SRCS publickeyexample.cpp)
|
||||
|
||||
add_executable(publickeyexample ${publickeyexample_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(publickeyexample)
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Copyright (C) 2003 Justin Karneges <justin@affinix.com>
|
||||
Copyright (C) 2005 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
// We need to ensure that we have certificate handling support
|
||||
if (!QCA::isSupported("cert")) {
|
||||
std::cout << "Sorry, no PKI certificate support" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read in a private key
|
||||
QCA::PrivateKey privKey;
|
||||
QCA::ConvertResult convRes;
|
||||
QCA::SecureArray passPhrase = "start";
|
||||
privKey = QCA::PrivateKey::fromPEMFile(QStringLiteral("Userkey.pem"), passPhrase, &convRes);
|
||||
if (convRes != QCA::ConvertGood) {
|
||||
std::cout << "Sorry, could not import Private Key" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read in a matching public key cert
|
||||
// you could also build this using the fromPEMFile() method
|
||||
QCA::Certificate pubCert(QStringLiteral("User.pem"));
|
||||
if (pubCert.isNull()) {
|
||||
std::cout << "Sorry, could not import public key certificate" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
// We are building the certificate into a SecureMessageKey object, via a
|
||||
// CertificateChain
|
||||
QCA::SecureMessageKey secMsgKey;
|
||||
QCA::CertificateChain chain;
|
||||
chain += pubCert;
|
||||
secMsgKey.setX509CertificateChain(chain);
|
||||
|
||||
// build up a SecureMessage object, based on our public key certificate
|
||||
QCA::CMS cms;
|
||||
QCA::SecureMessage msg(&cms);
|
||||
msg.setRecipient(secMsgKey);
|
||||
|
||||
// Some plain text - we use the first command line argument if provided
|
||||
QByteArray plainText = (argc >= 2) ? argv[1] : "What do ya want for nuthin'";
|
||||
|
||||
// Now use the SecureMessage object to encrypt the plain text.
|
||||
msg.startEncrypt();
|
||||
msg.update(plainText);
|
||||
msg.end();
|
||||
// I think it is reasonable to wait for 1 second for this
|
||||
msg.waitForFinished(1000);
|
||||
|
||||
// check to see if it worked
|
||||
if (!msg.success()) {
|
||||
std::cout << "Error encrypting: " << msg.errorCode() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// get the result
|
||||
QCA::SecureArray cipherText = msg.read();
|
||||
QCA::Base64 enc;
|
||||
std::cout << plainText.data() << " encrypts to (in base 64): ";
|
||||
std::cout << qPrintable(enc.arrayToString(cipherText)) << std::endl;
|
||||
|
||||
// Show we can decrypt it with the private key
|
||||
if (!privKey.canDecrypt()) {
|
||||
std::cout << "Private key cannot be used to decrypt" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
QCA::SecureArray plainTextResult;
|
||||
if (0 == privKey.decrypt(cipherText, &plainTextResult, QCA::EME_PKCS1_OAEP)) {
|
||||
std::cout << "Decryption process failed" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << qPrintable(enc.arrayToString(cipherText));
|
||||
std::cout << " (in base 64) decrypts to: ";
|
||||
std::cout << plainTextResult.data() << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
set(randomtest_bin_SRCS randomtest.cpp)
|
||||
|
||||
add_executable(randomtest ${randomtest_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(randomtest)
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright (C) 2004, 2006 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// QtCrypto has the declarations for all of QCA
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the Initializer object sets things up, and
|
||||
// also does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
qDebug() << "This example generates random numbers";
|
||||
|
||||
int randInt;
|
||||
// This is the standard way to generate a random integer.
|
||||
randInt = QCA::Random::randomInt();
|
||||
qDebug() << "A random number: " << randInt;
|
||||
|
||||
// If you wanted a random character (octet), you could
|
||||
// use something like:
|
||||
unsigned char randChar;
|
||||
randChar = QCA::Random::randomChar();
|
||||
// It might not be printable, so this may not produce output
|
||||
std::cout << "A random character: " << randChar << std::endl;
|
||||
|
||||
QCA::SecureArray tenBytes(10);
|
||||
// If you need more random values, you may want to
|
||||
// get an array, as shown below.
|
||||
tenBytes = QCA::Random::randomArray(10);
|
||||
|
||||
// To make this viewable, we convert to hexadecimal.
|
||||
std::cout << "A random 10 byte array (in hex): ";
|
||||
std::cout << qPrintable(QCA::Hex().arrayToString(tenBytes)) << std::endl;
|
||||
|
||||
// Under some circumstances, you may want to create a
|
||||
// Random object, rather than a static public member function.
|
||||
// This isn't normally the easiest way, but it does work
|
||||
QCA::Random myRandomObject;
|
||||
randChar = myRandomObject.nextByte();
|
||||
tenBytes = myRandomObject.nextBytes(10);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
set(rsatest_bin_SRCS rsatest.cpp)
|
||||
|
||||
add_executable(rsatest ${rsatest_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(rsatest)
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
Copyright (C) 2003 Justin Karneges <justin@affinix.com>
|
||||
Copyright (C) 2005 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// The Initializer object sets things up, and also
|
||||
// does cleanup when it goes out of scope
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
// we use the first argument if provided, or
|
||||
// use "hello" if no arguments
|
||||
QCA::SecureArray arg = (argc >= 2) ? argv[1] : "hello";
|
||||
|
||||
// We demonstrate PEM usage here, so we need to test for
|
||||
// supportedIOTypes, not just supportedTypes
|
||||
if (!QCA::isSupported("pkey") || !QCA::PKey::supportedIOTypes().contains(QCA::PKey::RSA))
|
||||
std::cout << "RSA not supported!\n";
|
||||
else {
|
||||
// When creating a public / private key pair, you make the
|
||||
// private key, and then extract the public key component from it
|
||||
// Using RSA is very common, however DSA can provide equivalent
|
||||
// signature/verification. This example applies to DSA to the
|
||||
// extent that the operations work on that key type.
|
||||
|
||||
// QCA provides KeyGenerator as a convenient source of new keys,
|
||||
// however you could also import an existing key instead.
|
||||
QCA::PrivateKey seckey = QCA::KeyGenerator().createRSA(1024);
|
||||
if (seckey.isNull()) {
|
||||
std::cout << "Failed to make private RSA key" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
QCA::PublicKey pubkey = seckey.toPublicKey();
|
||||
|
||||
// check if the key can encrypt
|
||||
if (!pubkey.canEncrypt()) {
|
||||
std::cout << "Error: this kind of key cannot encrypt" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// encrypt some data - note that only the public key is required
|
||||
// you must also choose the algorithm to be used
|
||||
QCA::SecureArray result = pubkey.encrypt(arg, QCA::EME_PKCS1_OAEP);
|
||||
if (result.isEmpty()) {
|
||||
std::cout << "Error encrypting" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// output the encrypted data
|
||||
QString rstr = QCA::arrayToHex(result.toByteArray());
|
||||
std::cout << "\"" << arg.data() << "\" encrypted with RSA is \"";
|
||||
std::cout << qPrintable(rstr) << "\"" << std::endl;
|
||||
|
||||
// save the private key - in a real example, make sure this goes
|
||||
// somewhere secure and has a good pass phrase
|
||||
// You can use the same technique with the public key too.
|
||||
QCA::SecureArray passPhrase = "pass phrase";
|
||||
seckey.toPEMFile(QStringLiteral("keyprivate.pem"), passPhrase);
|
||||
|
||||
// Read that key back in, checking if the read succeeded
|
||||
QCA::ConvertResult conversionResult;
|
||||
QCA::PrivateKey privateKey =
|
||||
QCA::PrivateKey::fromPEMFile(QStringLiteral("keyprivate.pem"), passPhrase, &conversionResult);
|
||||
if (!(QCA::ConvertGood == conversionResult)) {
|
||||
std::cout << "Private key read failed" << std::endl;
|
||||
}
|
||||
|
||||
// now decrypt that encrypted data using the private key that
|
||||
// we read in. The algorithm is the same.
|
||||
QCA::SecureArray decrypt;
|
||||
if (0 == privateKey.decrypt(result, &decrypt, QCA::EME_PKCS1_OAEP)) {
|
||||
std::cout << "Error decrypting.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// output the resulting decrypted string
|
||||
std::cout << "\"" << qPrintable(rstr) << "\" decrypted with RSA is \"";
|
||||
std::cout << decrypt.data() << "\"" << std::endl;
|
||||
|
||||
// Some private keys can also be used for producing signatures
|
||||
if (!privateKey.canSign()) {
|
||||
std::cout << "Error: this kind of key cannot sign" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
privateKey.startSign(QCA::EMSA3_MD5);
|
||||
privateKey.update(arg); // just reuse the same message
|
||||
QByteArray argSig = privateKey.signature();
|
||||
|
||||
// instead of using the startSign(), update(), signature() calls,
|
||||
// you may be better doing the whole thing in one go, using the
|
||||
// signMessage call. Of course you need the whole message in one
|
||||
// hit, which may or may not be a problem
|
||||
|
||||
// output the resulting signature
|
||||
rstr = QCA::arrayToHex(argSig);
|
||||
std::cout << "Signature for \"" << arg.data() << "\" using RSA, is ";
|
||||
std::cout << "\"" << qPrintable(rstr) << "\"" << std::endl;
|
||||
|
||||
// to check a signature, we must check that the key is
|
||||
// appropriate
|
||||
if (pubkey.canVerify()) {
|
||||
pubkey.startVerify(QCA::EMSA3_MD5);
|
||||
pubkey.update(arg);
|
||||
if (pubkey.validSignature(argSig)) {
|
||||
std::cout << "Signature is valid" << std::endl;
|
||||
} else {
|
||||
std::cout << "Bad signature" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// We can also do the verification in a single step if we
|
||||
// have all the message
|
||||
if (pubkey.canVerify() && pubkey.verifyMessage(arg, argSig, QCA::EMSA3_MD5)) {
|
||||
std::cout << "Signature is valid" << std::endl;
|
||||
} else {
|
||||
std::cout << "Signature could not be verified" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
set(saslclient_bin_SRCS saslclient.cpp)
|
||||
|
||||
add_executable(saslclient ${saslclient_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(saslclient)
|
||||
target_link_libraries(saslclient Qt${QT_MAJOR_VERSION}::Network)
|
||||
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
Copyright (C) 2003-2008 Justin Karneges <justin@affinix.com>
|
||||
Copyright (C) 2006 Michail Pishchagin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QTimer>
|
||||
#include <cstdio>
|
||||
|
||||
// QtCrypto has the declarations for all of QCA
|
||||
#include <QtCrypto>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
static QString prompt(const QString &s)
|
||||
{
|
||||
printf("* %s ", qPrintable(s));
|
||||
fflush(stdout);
|
||||
char line[256];
|
||||
fgets(line, 255, stdin);
|
||||
QString result = QString::fromLatin1(line);
|
||||
if (result[result.length() - 1] == QLatin1Char('\n'))
|
||||
result.truncate(result.length() - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
static QString socketErrorToString(QAbstractSocket::SocketError x)
|
||||
{
|
||||
QString s;
|
||||
switch (x) {
|
||||
case QAbstractSocket::ConnectionRefusedError:
|
||||
s = QStringLiteral("connection refused or timed out");
|
||||
break;
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
s = QStringLiteral("remote host closed the connection");
|
||||
break;
|
||||
case QAbstractSocket::HostNotFoundError:
|
||||
s = QStringLiteral("host not found");
|
||||
break;
|
||||
case QAbstractSocket::SocketAccessError:
|
||||
s = QStringLiteral("access error");
|
||||
break;
|
||||
case QAbstractSocket::SocketResourceError:
|
||||
s = QStringLiteral("too many sockets");
|
||||
break;
|
||||
case QAbstractSocket::SocketTimeoutError:
|
||||
s = QStringLiteral("operation timed out");
|
||||
break;
|
||||
case QAbstractSocket::DatagramTooLargeError:
|
||||
s = QStringLiteral("datagram was larger than system limit");
|
||||
break;
|
||||
case QAbstractSocket::NetworkError:
|
||||
s = QStringLiteral("network error");
|
||||
break;
|
||||
case QAbstractSocket::AddressInUseError:
|
||||
s = QStringLiteral("address is already in use");
|
||||
break;
|
||||
case QAbstractSocket::SocketAddressNotAvailableError:
|
||||
s = QStringLiteral("address does not belong to the host");
|
||||
break;
|
||||
case QAbstractSocket::UnsupportedSocketOperationError:
|
||||
s = QStringLiteral("operation is not supported by the local operating system");
|
||||
break;
|
||||
default:
|
||||
s = QStringLiteral("unknown socket error");
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static QString saslAuthConditionToString(QCA::SASL::AuthCondition x)
|
||||
{
|
||||
QString s;
|
||||
switch (x) {
|
||||
case QCA::SASL::NoMechanism:
|
||||
s = QStringLiteral("no appropriate mechanism could be negotiated");
|
||||
break;
|
||||
case QCA::SASL::BadProtocol:
|
||||
s = QStringLiteral("bad SASL protocol");
|
||||
break;
|
||||
case QCA::SASL::BadServer:
|
||||
s = QStringLiteral("server failed mutual authentication");
|
||||
break;
|
||||
// AuthFail or unknown (including those defined for server only)
|
||||
default:
|
||||
s = QStringLiteral("generic authentication failure");
|
||||
break;
|
||||
};
|
||||
return s;
|
||||
}
|
||||
|
||||
class ClientTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QString host, proto, authzid, realm, user, pass;
|
||||
int port;
|
||||
bool no_authzid, no_realm;
|
||||
int mode; // 0 = receive mechanism list, 1 = sasl negotiation, 2 = app
|
||||
QTcpSocket *sock;
|
||||
QCA::SASL *sasl;
|
||||
QByteArray inbuf;
|
||||
bool sock_done;
|
||||
int waitCycles;
|
||||
|
||||
public:
|
||||
ClientTest(const QString &_host,
|
||||
int _port,
|
||||
const QString &_proto,
|
||||
const QString &_authzid,
|
||||
const QString &_realm,
|
||||
const QString &_user,
|
||||
const QString &_pass,
|
||||
bool _no_authzid,
|
||||
bool _no_realm)
|
||||
: host(_host)
|
||||
, proto(_proto)
|
||||
, authzid(_authzid)
|
||||
, realm(_realm)
|
||||
, user(_user)
|
||||
, pass(_pass)
|
||||
, port(_port)
|
||||
, no_authzid(_no_authzid)
|
||||
, no_realm(_no_realm)
|
||||
, sock_done(false)
|
||||
, waitCycles(0)
|
||||
{
|
||||
sock = new QTcpSocket(this);
|
||||
connect(sock, &QTcpSocket::connected, this, &ClientTest::sock_connected);
|
||||
connect(sock, &QTcpSocket::readyRead, this, &ClientTest::sock_readyRead);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
connect(sock, &QTcpSocket::errorOccurred, this, &ClientTest::sock_error);
|
||||
#else
|
||||
connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &ClientTest::sock_error);
|
||||
#endif
|
||||
|
||||
sasl = new QCA::SASL(this);
|
||||
connect(sasl, &QCA::SASL::clientStarted, this, &ClientTest::sasl_clientFirstStep);
|
||||
connect(sasl, &QCA::SASL::nextStep, this, &ClientTest::sasl_nextStep);
|
||||
connect(sasl, &QCA::SASL::needParams, this, &ClientTest::sasl_needParams);
|
||||
connect(sasl, &QCA::SASL::authenticated, this, &ClientTest::sasl_authenticated);
|
||||
connect(sasl, &QCA::SASL::readyRead, this, &ClientTest::sasl_readyRead);
|
||||
connect(sasl, &QCA::SASL::readyReadOutgoing, this, &ClientTest::sasl_readyReadOutgoing);
|
||||
connect(sasl, &QCA::SASL::error, this, &ClientTest::sasl_error);
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
void start()
|
||||
{
|
||||
mode = 0; // mech list mode
|
||||
|
||||
int flags = 0;
|
||||
flags |= QCA::SASL::AllowPlain;
|
||||
flags |= QCA::SASL::AllowAnonymous;
|
||||
sasl->setConstraints((QCA::SASL::AuthFlags)flags, 0, 256);
|
||||
|
||||
if (!user.isEmpty())
|
||||
sasl->setUsername(user);
|
||||
if (!authzid.isEmpty())
|
||||
sasl->setAuthzid(authzid);
|
||||
if (!pass.isEmpty())
|
||||
sasl->setPassword(pass.toUtf8());
|
||||
if (!realm.isEmpty())
|
||||
sasl->setRealm(realm);
|
||||
|
||||
printf("Connecting to %s:%d, for protocol %s\n", qPrintable(host), port, qPrintable(proto));
|
||||
sock->connectToHost(host, port);
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void quit();
|
||||
|
||||
private Q_SLOTS:
|
||||
void sock_connected()
|
||||
{
|
||||
printf("Connected to server. Awaiting mechanism list...\n");
|
||||
}
|
||||
|
||||
void sock_error(QAbstractSocket::SocketError x)
|
||||
{
|
||||
if (x == QAbstractSocket::RemoteHostClosedError) {
|
||||
if (mode == 2) // app mode, where disconnect means completion
|
||||
{
|
||||
sock_done = true;
|
||||
tryFinished();
|
||||
return;
|
||||
} else // any other mode, where disconnect is an error
|
||||
{
|
||||
printf("Error: server closed connection unexpectedly.\n");
|
||||
emit quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Error: socket: %s\n", qPrintable(socketErrorToString(x)));
|
||||
emit quit();
|
||||
}
|
||||
|
||||
void sock_readyRead()
|
||||
{
|
||||
if (mode == 2) // app mode
|
||||
{
|
||||
QByteArray a = sock->readAll();
|
||||
printf("Read %d bytes\n", int(a.size()));
|
||||
|
||||
// there is a possible flaw in the qca 2.0 api, in
|
||||
// that if sasl data is received from the peer
|
||||
// followed by a disconnect from the peer, there is
|
||||
// no clear approach to salvaging the bytes. tls is
|
||||
// not affected because tls has the concept of
|
||||
// closing a session. with sasl, there is no
|
||||
// closing, and since the qca api is asynchronous,
|
||||
// we could potentially wait forever for decoded
|
||||
// data, if the last write was a partial packet.
|
||||
//
|
||||
// for now, we can perform a simple workaround of
|
||||
// waiting at least three event loop cycles for
|
||||
// decoded data before giving up and assuming the
|
||||
// last write was partial. the fact is, all current
|
||||
// qca sasl providers respond within this time
|
||||
// frame, so this fix should work fine for now. in
|
||||
// qca 2.1, we should revise the api to handle this
|
||||
// situation better.
|
||||
//
|
||||
// further note: i guess this only affects application
|
||||
// protocols that have no close message of their
|
||||
// own, and rely on the tcp-level close. examples
|
||||
// are http, and of course this qcatest protocol.
|
||||
if (waitCycles == 0) {
|
||||
waitCycles = 3;
|
||||
QMetaObject::invokeMethod(this, "waitWriteIncoming", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
sasl->writeIncoming(a);
|
||||
} else // mech list or sasl negotiation mode
|
||||
{
|
||||
if (sock->canReadLine()) {
|
||||
QString line = QString::fromLatin1(sock->readLine());
|
||||
line.truncate(line.length() - 1); // chop the newline
|
||||
handleLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sasl_clientFirstStep(bool clientInit, const QByteArray &clientInitData)
|
||||
{
|
||||
printf("Choosing mech: %s\n", qPrintable(sasl->mechanism()));
|
||||
QString line = sasl->mechanism();
|
||||
if (clientInit) {
|
||||
line += QLatin1Char(' ');
|
||||
line += arrayToString(clientInitData);
|
||||
}
|
||||
sendLine(line);
|
||||
}
|
||||
|
||||
void sasl_nextStep(const QByteArray &stepData)
|
||||
{
|
||||
QString line = QStringLiteral("C");
|
||||
if (!stepData.isEmpty()) {
|
||||
line += QLatin1Char(',');
|
||||
line += arrayToString(stepData);
|
||||
}
|
||||
sendLine(line);
|
||||
}
|
||||
|
||||
void sasl_needParams(const QCA::SASL::Params ¶ms)
|
||||
{
|
||||
if (params.needUsername()) {
|
||||
user = prompt(QStringLiteral("Username:"));
|
||||
sasl->setUsername(user);
|
||||
}
|
||||
|
||||
if (params.canSendAuthzid() && !no_authzid) {
|
||||
authzid = prompt(QStringLiteral("Authorize As (enter to skip):"));
|
||||
if (!authzid.isEmpty())
|
||||
sasl->setAuthzid(authzid);
|
||||
}
|
||||
|
||||
if (params.needPassword()) {
|
||||
QCA::ConsolePrompt prompt;
|
||||
prompt.getHidden(QStringLiteral("* Password"));
|
||||
prompt.waitForFinished();
|
||||
QCA::SecureArray pass = prompt.result();
|
||||
sasl->setPassword(pass);
|
||||
}
|
||||
|
||||
if (params.canSendRealm() && !no_realm) {
|
||||
QStringList realms = sasl->realmList();
|
||||
printf("Available realms:\n");
|
||||
if (realms.isEmpty())
|
||||
printf(" (none specified)\n");
|
||||
foreach (const QString &s, realms)
|
||||
printf(" %s\n", qPrintable(s));
|
||||
realm = prompt(QStringLiteral("Realm (enter to skip):"));
|
||||
if (!realm.isEmpty())
|
||||
sasl->setRealm(realm);
|
||||
}
|
||||
|
||||
sasl->continueAfterParams();
|
||||
}
|
||||
|
||||
void sasl_authenticated()
|
||||
{
|
||||
printf("SASL success!\n");
|
||||
printf("SSF: %d\n", sasl->ssf());
|
||||
}
|
||||
|
||||
void sasl_readyRead()
|
||||
{
|
||||
QByteArray a = sasl->read();
|
||||
inbuf += a;
|
||||
processInbuf();
|
||||
}
|
||||
|
||||
void sasl_readyReadOutgoing()
|
||||
{
|
||||
QByteArray a = sasl->readOutgoing();
|
||||
sock->write(a);
|
||||
}
|
||||
|
||||
void sasl_error()
|
||||
{
|
||||
int e = sasl->errorCode();
|
||||
if (e == QCA::SASL::ErrorInit)
|
||||
printf("Error: sasl: initialization failed.\n");
|
||||
else if (e == QCA::SASL::ErrorHandshake)
|
||||
printf("Error: sasl: %s.\n", qPrintable(saslAuthConditionToString(sasl->authCondition())));
|
||||
else if (e == QCA::SASL::ErrorCrypt)
|
||||
printf("Error: sasl: broken security layer.\n");
|
||||
else
|
||||
printf("Error: sasl: unknown error.\n");
|
||||
|
||||
emit quit();
|
||||
}
|
||||
|
||||
void waitWriteIncoming()
|
||||
{
|
||||
--waitCycles;
|
||||
if (waitCycles > 0) {
|
||||
QMetaObject::invokeMethod(this, "waitWriteIncoming", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
tryFinished();
|
||||
}
|
||||
|
||||
private:
|
||||
void tryFinished()
|
||||
{
|
||||
if (sock_done && waitCycles == 0) {
|
||||
printf("Finished, server closed connection.\n");
|
||||
|
||||
// if we give up on waiting for a response to
|
||||
// writeIncoming, then it might come late. in
|
||||
// theory this shouldn't happen if we wait enough
|
||||
// cycles, but if one were to arrive then it could
|
||||
// occur between the request to quit the app and
|
||||
// the actual quit of the app. to assist with
|
||||
// debugging, then, we'll explicitly stop listening
|
||||
// for signals here. otherwise the response may
|
||||
// still be received and displayed, giving a false
|
||||
// sense of correctness.
|
||||
sasl->disconnect(this);
|
||||
|
||||
emit quit();
|
||||
}
|
||||
}
|
||||
|
||||
QString arrayToString(const QByteArray &ba)
|
||||
{
|
||||
return QCA::Base64().arrayToString(ba);
|
||||
}
|
||||
|
||||
QByteArray stringToArray(const QString &s)
|
||||
{
|
||||
return QCA::Base64().stringToArray(s).toByteArray();
|
||||
}
|
||||
|
||||
void sendLine(const QString &line)
|
||||
{
|
||||
printf("Writing: {%s}\n", qPrintable(line));
|
||||
QString s = line + QLatin1Char('\n');
|
||||
QByteArray a = s.toUtf8();
|
||||
if (mode == 2) // app mode
|
||||
sasl->write(a); // write to sasl
|
||||
else // mech list or sasl negotiation
|
||||
sock->write(a); // write to socket
|
||||
}
|
||||
|
||||
void processInbuf()
|
||||
{
|
||||
// collect completed lines from inbuf
|
||||
QStringList list;
|
||||
int at;
|
||||
while ((at = inbuf.indexOf('\n')) != -1) {
|
||||
list += QString::fromUtf8(inbuf.mid(0, at));
|
||||
inbuf = inbuf.mid(at + 1);
|
||||
}
|
||||
|
||||
// process the lines
|
||||
foreach (const QString &line, list)
|
||||
handleLine(line);
|
||||
}
|
||||
|
||||
void handleLine(const QString &line)
|
||||
{
|
||||
printf("Reading: [%s]\n", qPrintable(line));
|
||||
if (mode == 0) {
|
||||
// first line is the method list
|
||||
const QStringList mechlist = line.split(QLatin1Char(' '));
|
||||
mode = 1; // switch to sasl negotiation mode
|
||||
sasl->startClient(proto, host, mechlist);
|
||||
} else if (mode == 1) {
|
||||
QString type, rest;
|
||||
int n = line.indexOf(QLatin1Char(','));
|
||||
if (n != -1) {
|
||||
type = line.mid(0, n);
|
||||
rest = line.mid(n + 1);
|
||||
} else
|
||||
type = line;
|
||||
|
||||
if (type == QLatin1String("C")) {
|
||||
sasl->putStep(stringToArray(rest));
|
||||
} else if (type == QLatin1String("E")) {
|
||||
if (!rest.isEmpty())
|
||||
printf("Error: server says: %s.\n", qPrintable(rest));
|
||||
else
|
||||
printf("Error: server error, unspecified.\n");
|
||||
emit quit();
|
||||
return;
|
||||
} else if (type == QLatin1String("A")) {
|
||||
printf("Authentication success.\n");
|
||||
mode = 2; // switch to app mode
|
||||
|
||||
// at this point, the server may send us text
|
||||
// lines for us to display and then close.
|
||||
|
||||
sock_readyRead(); // any extra data?
|
||||
return;
|
||||
} else {
|
||||
printf("Error: Bad format from peer, closing.\n");
|
||||
emit quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void usage()
|
||||
{
|
||||
printf("usage: saslclient (options) host(:port) (user) (pass)\n");
|
||||
printf("options: --proto=x, --authzid=x, --realm=x\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCA::Initializer init;
|
||||
QCoreApplication qapp(argc, argv);
|
||||
|
||||
QStringList args = qapp.arguments();
|
||||
args.removeFirst();
|
||||
|
||||
// options
|
||||
QString proto = QStringLiteral("qcatest"); // default protocol
|
||||
QString authzid, realm;
|
||||
bool no_authzid = false;
|
||||
bool no_realm = false;
|
||||
for (int n = 0; n < args.count(); ++n) {
|
||||
if (!args[n].startsWith(QLatin1String("--")))
|
||||
continue;
|
||||
|
||||
QString opt = args[n].mid(2);
|
||||
QString var, val;
|
||||
int at = opt.indexOf(QLatin1Char('='));
|
||||
if (at != -1) {
|
||||
var = opt.mid(0, at);
|
||||
val = opt.mid(at + 1);
|
||||
} else
|
||||
var = opt;
|
||||
|
||||
if (var == QLatin1String("proto")) {
|
||||
proto = val;
|
||||
} else if (var == QLatin1String("authzid")) {
|
||||
// specifying empty authzid means force unspecified
|
||||
if (val.isEmpty())
|
||||
no_authzid = true;
|
||||
else
|
||||
authzid = val;
|
||||
} else if (var == QLatin1String("realm")) {
|
||||
// specifying empty realm means force unspecified
|
||||
if (val.isEmpty())
|
||||
no_realm = true;
|
||||
else
|
||||
realm = val;
|
||||
}
|
||||
|
||||
args.removeAt(n);
|
||||
--n; // adjust position
|
||||
}
|
||||
|
||||
if (args.count() < 1) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString host, user, pass;
|
||||
int port = 8001; // default port
|
||||
|
||||
QString hostinput = args[0];
|
||||
if (args.count() >= 2)
|
||||
user = args[1];
|
||||
if (args.count() >= 3)
|
||||
pass = args[2];
|
||||
|
||||
int at = hostinput.indexOf(QLatin1Char(':'));
|
||||
if (at != -1) {
|
||||
host = hostinput.mid(0, at);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 2)
|
||||
port = QStringView(hostinput).mid(at + 1).toInt();
|
||||
#else
|
||||
port = hostinput.midRef(at + 1).toInt();
|
||||
#endif
|
||||
} else
|
||||
host = hostinput;
|
||||
|
||||
if (!QCA::isSupported("sasl")) {
|
||||
printf("Error: SASL support not found.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ClientTest client(host, port, proto, authzid, realm, user, pass, no_authzid, no_realm);
|
||||
QObject::connect(&client, &ClientTest::quit, &qapp, &QCoreApplication::quit);
|
||||
QTimer::singleShot(0, &client, &ClientTest::start);
|
||||
qapp.exec();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "saslclient.moc"
|
||||
@@ -0,0 +1,6 @@
|
||||
set(saslserver_bin_SRCS saslserver.cpp)
|
||||
|
||||
add_executable(saslserver ${saslserver_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(saslserver)
|
||||
target_link_libraries(saslserver Qt${QT_MAJOR_VERSION}::Network)
|
||||
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
Copyright (C) 2003-2008 Justin Karneges <justin@affinix.com>
|
||||
Copyright (C) 2006 Michail Pishchagin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QTimer>
|
||||
#include <cstdio>
|
||||
|
||||
// QtCrypto has the declarations for all of QCA
|
||||
#include <QtCrypto>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
static QString socketErrorToString(QAbstractSocket::SocketError x)
|
||||
{
|
||||
QString s;
|
||||
switch (x) {
|
||||
case QAbstractSocket::ConnectionRefusedError:
|
||||
s = QStringLiteral("connection refused or timed out");
|
||||
break;
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
s = QStringLiteral("remote host closed the connection");
|
||||
break;
|
||||
case QAbstractSocket::HostNotFoundError:
|
||||
s = QStringLiteral("host not found");
|
||||
break;
|
||||
case QAbstractSocket::SocketAccessError:
|
||||
s = QStringLiteral("access error");
|
||||
break;
|
||||
case QAbstractSocket::SocketResourceError:
|
||||
s = QStringLiteral("too many sockets");
|
||||
break;
|
||||
case QAbstractSocket::SocketTimeoutError:
|
||||
s = QStringLiteral("operation timed out");
|
||||
break;
|
||||
case QAbstractSocket::DatagramTooLargeError:
|
||||
s = QStringLiteral("datagram was larger than system limit");
|
||||
break;
|
||||
case QAbstractSocket::NetworkError:
|
||||
s = QStringLiteral("network error");
|
||||
break;
|
||||
case QAbstractSocket::AddressInUseError:
|
||||
s = QStringLiteral("address is already in use");
|
||||
break;
|
||||
case QAbstractSocket::SocketAddressNotAvailableError:
|
||||
s = QStringLiteral("address does not belong to the host");
|
||||
break;
|
||||
case QAbstractSocket::UnsupportedSocketOperationError:
|
||||
s = QStringLiteral("operation is not supported by the local operating system");
|
||||
break;
|
||||
default:
|
||||
s = QStringLiteral("unknown socket error");
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static QString saslAuthConditionToString(QCA::SASL::AuthCondition x)
|
||||
{
|
||||
QString s;
|
||||
switch (x) {
|
||||
case QCA::SASL::NoMechanism:
|
||||
s = QStringLiteral("no appropriate mechanism could be negotiated");
|
||||
break;
|
||||
case QCA::SASL::BadProtocol:
|
||||
s = QStringLiteral("bad SASL protocol");
|
||||
break;
|
||||
case QCA::SASL::BadAuth:
|
||||
s = QStringLiteral("authentication failed");
|
||||
break;
|
||||
case QCA::SASL::NoAuthzid:
|
||||
s = QStringLiteral("authorization failed");
|
||||
break;
|
||||
case QCA::SASL::TooWeak:
|
||||
s = QStringLiteral("mechanism too weak for this user");
|
||||
break;
|
||||
case QCA::SASL::NeedEncrypt:
|
||||
s = QStringLiteral("encryption is needed to use this mechanism");
|
||||
break;
|
||||
case QCA::SASL::Expired:
|
||||
s = QStringLiteral("passphrase expired");
|
||||
break;
|
||||
case QCA::SASL::Disabled:
|
||||
s = QStringLiteral("account is disabled");
|
||||
break;
|
||||
case QCA::SASL::NoUser:
|
||||
s = QStringLiteral("user not found");
|
||||
break;
|
||||
case QCA::SASL::RemoteUnavailable:
|
||||
s = QStringLiteral("needed remote service is unavailable");
|
||||
break;
|
||||
// AuthFail or unknown (including those defined for client only)
|
||||
default:
|
||||
s = QStringLiteral("generic authentication failure");
|
||||
break;
|
||||
};
|
||||
return s;
|
||||
}
|
||||
|
||||
// --- ServerTest declaration
|
||||
|
||||
class ServerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QString host, proto, realm, str;
|
||||
int port;
|
||||
QTcpServer *tcpServer;
|
||||
QList<int> ids;
|
||||
|
||||
public:
|
||||
ServerTest(const QString &_host, int _port, const QString &_proto, const QString &_realm, const QString &_str);
|
||||
|
||||
int reserveId();
|
||||
void releaseId(int id);
|
||||
|
||||
public Q_SLOTS:
|
||||
void start();
|
||||
|
||||
Q_SIGNALS:
|
||||
void quit();
|
||||
|
||||
private Q_SLOTS:
|
||||
void server_newConnection();
|
||||
};
|
||||
|
||||
// --- ServerTestHandler
|
||||
|
||||
class ServerTestHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
ServerTest *serverTest;
|
||||
QTcpSocket *sock;
|
||||
QCA::SASL *sasl;
|
||||
int id;
|
||||
QString host, proto, realm, str;
|
||||
int mode; // 0 = receive mechanism list, 1 = sasl negotiation, 2 = app
|
||||
int toWrite;
|
||||
|
||||
public:
|
||||
ServerTestHandler(ServerTest *_serverTest,
|
||||
QTcpSocket *_sock,
|
||||
const QString &_host,
|
||||
const QString &_proto,
|
||||
const QString &_realm,
|
||||
const QString &_str)
|
||||
: serverTest(_serverTest)
|
||||
, sock(_sock)
|
||||
, host(_host)
|
||||
, proto(_proto)
|
||||
, realm(_realm)
|
||||
, str(_str)
|
||||
{
|
||||
id = serverTest->reserveId();
|
||||
|
||||
sock->setParent(this);
|
||||
connect(sock, &QTcpSocket::disconnected, this, &ServerTestHandler::sock_disconnected);
|
||||
connect(sock, &QTcpSocket::readyRead, this, &ServerTestHandler::sock_readyRead);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
connect(sock, &QTcpSocket::errorOccurred, this, &ServerTestHandler::sock_error);
|
||||
#else
|
||||
connect(sock,
|
||||
QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error),
|
||||
this,
|
||||
&ServerTestHandler::sock_error);
|
||||
#endif
|
||||
connect(sock, &QTcpSocket::bytesWritten, this, &ServerTestHandler::sock_bytesWritten);
|
||||
|
||||
sasl = new QCA::SASL(this);
|
||||
connect(sasl, &QCA::SASL::authCheck, this, &ServerTestHandler::sasl_authCheck);
|
||||
connect(sasl, &QCA::SASL::nextStep, this, &ServerTestHandler::sasl_nextStep);
|
||||
connect(sasl, &QCA::SASL::authenticated, this, &ServerTestHandler::sasl_authenticated);
|
||||
connect(sasl, &QCA::SASL::readyRead, this, &ServerTestHandler::sasl_readyRead);
|
||||
connect(sasl, &QCA::SASL::readyReadOutgoing, this, &ServerTestHandler::sasl_readyReadOutgoing);
|
||||
connect(sasl, &QCA::SASL::error, this, &ServerTestHandler::sasl_error);
|
||||
connect(sasl, &QCA::SASL::serverStarted, this, &ServerTestHandler::sasl_serverStarted);
|
||||
|
||||
mode = 0; // mech list mode
|
||||
toWrite = 0;
|
||||
|
||||
int flags = 0;
|
||||
flags |= QCA::SASL::AllowPlain;
|
||||
flags |= QCA::SASL::AllowAnonymous;
|
||||
sasl->setConstraints((QCA::SASL::AuthFlags)flags, 0, 256);
|
||||
|
||||
printf("%d: Connection received! Starting SASL handshake...\n", id);
|
||||
sasl->startServer(proto, host, realm);
|
||||
}
|
||||
|
||||
~ServerTestHandler() override
|
||||
{
|
||||
serverTest->releaseId(id);
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void sasl_serverStarted()
|
||||
{
|
||||
sendLine(sasl->mechanismList().join(QStringLiteral(" ")));
|
||||
}
|
||||
|
||||
void sock_disconnected()
|
||||
{
|
||||
printf("%d: Connection closed.\n", id);
|
||||
discard();
|
||||
}
|
||||
|
||||
void sock_error(QAbstractSocket::SocketError x)
|
||||
{
|
||||
if (x == QAbstractSocket::RemoteHostClosedError) {
|
||||
printf("%d: Error: client closed connection unexpectedly.\n", id);
|
||||
discard();
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%d: Error: socket: %s\n", id, qPrintable(socketErrorToString(x)));
|
||||
discard();
|
||||
}
|
||||
|
||||
void sock_readyRead()
|
||||
{
|
||||
if (sock->canReadLine()) {
|
||||
QString line = QString::fromLatin1(sock->readLine());
|
||||
line.truncate(line.length() - 1); // chop the newline
|
||||
handleLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
void sock_bytesWritten(qint64 x)
|
||||
{
|
||||
if (mode == 2) // app mode
|
||||
{
|
||||
toWrite -= sasl->convertBytesWritten(x);
|
||||
if (toWrite == 0) {
|
||||
printf("%d: Sent, closing.\n", id);
|
||||
sock->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sasl_nextStep(const QByteArray &stepData)
|
||||
{
|
||||
QString line = QStringLiteral("C");
|
||||
if (!stepData.isEmpty()) {
|
||||
line += QLatin1Char(',');
|
||||
line += arrayToString(stepData);
|
||||
}
|
||||
sendLine(line);
|
||||
}
|
||||
|
||||
void sasl_authCheck(const QString &user, const QString &authzid)
|
||||
{
|
||||
printf("%d: AuthCheck: User: [%s], Authzid: [%s]\n", id, qPrintable(user), qPrintable(authzid));
|
||||
|
||||
// user - who has logged in, confirmed by sasl
|
||||
// authzid - the identity the user wishes to act as, which
|
||||
// could be another user or just any arbitrary string (in
|
||||
// XMPP, this field holds a Jabber ID, for example). this
|
||||
// field is not necessarily confirmed by sasl, and the
|
||||
// decision about whether the user can act as the authzid
|
||||
// must be made by the app.
|
||||
|
||||
// for this simple example program, we allow anyone to use
|
||||
// the service, and simply continue onward with the
|
||||
// negotiation.
|
||||
sasl->continueAfterAuthCheck();
|
||||
}
|
||||
|
||||
void sasl_authenticated()
|
||||
{
|
||||
sendLine(QStringLiteral("A"));
|
||||
printf("%d: Authentication success.\n", id);
|
||||
mode = 2; // switch to app mode
|
||||
printf("%d: SSF: %d\n", id, sasl->ssf());
|
||||
sendLine(str);
|
||||
}
|
||||
|
||||
void sasl_readyRead()
|
||||
{
|
||||
QByteArray a = sasl->read();
|
||||
printf("%d: Warning, client sent %d bytes unexpectedly.\n", id, int(a.size()));
|
||||
}
|
||||
|
||||
void sasl_readyReadOutgoing()
|
||||
{
|
||||
sock->write(sasl->readOutgoing());
|
||||
}
|
||||
|
||||
void sasl_error()
|
||||
{
|
||||
int e = sasl->errorCode();
|
||||
if (e == QCA::SASL::ErrorInit) {
|
||||
printf("%d: Error: sasl: initialization failed.\n", id);
|
||||
} else if (e == QCA::SASL::ErrorHandshake) {
|
||||
QString errstr = saslAuthConditionToString(sasl->authCondition());
|
||||
sendLine(QStringLiteral("E,") + errstr);
|
||||
printf("%d: Error: sasl: %s.\n", id, qPrintable(errstr));
|
||||
} else if (e == QCA::SASL::ErrorCrypt) {
|
||||
printf("%d: Error: sasl: broken security layer.\n", id);
|
||||
} else {
|
||||
printf("%d: Error: sasl: unknown error.\n", id);
|
||||
}
|
||||
|
||||
sock->close();
|
||||
}
|
||||
|
||||
private:
|
||||
void discard()
|
||||
{
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void handleLine(const QString &line)
|
||||
{
|
||||
printf("%d: Reading: [%s]\n", id, qPrintable(line));
|
||||
if (mode == 0) {
|
||||
int n = line.indexOf(QLatin1Char(' '));
|
||||
if (n != -1) {
|
||||
QString mech = line.mid(0, n);
|
||||
QString rest = QString::fromLatin1(line.mid(n + 1).toUtf8());
|
||||
sasl->putServerFirstStep(mech, stringToArray(rest));
|
||||
} else
|
||||
sasl->putServerFirstStep(line);
|
||||
++mode;
|
||||
} else if (mode == 1) {
|
||||
QString type, rest;
|
||||
int n = line.indexOf(QLatin1Char(','));
|
||||
if (n != -1) {
|
||||
type = line.mid(0, n);
|
||||
rest = line.mid(n + 1);
|
||||
} else {
|
||||
type = line;
|
||||
rest = QLatin1String("");
|
||||
}
|
||||
|
||||
if (type == QLatin1String("C")) {
|
||||
sasl->putStep(stringToArray(rest));
|
||||
} else {
|
||||
printf("%d: Bad format from peer, closing.\n", id);
|
||||
sock->close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString arrayToString(const QByteArray &ba)
|
||||
{
|
||||
QCA::Base64 encoder;
|
||||
return encoder.arrayToString(ba);
|
||||
}
|
||||
|
||||
QByteArray stringToArray(const QString &s)
|
||||
{
|
||||
QCA::Base64 decoder(QCA::Decode);
|
||||
return decoder.stringToArray(s).toByteArray();
|
||||
}
|
||||
|
||||
void sendLine(const QString &line)
|
||||
{
|
||||
printf("%d: Writing: {%s}\n", id, qPrintable(line));
|
||||
QString s = line + QLatin1Char('\n');
|
||||
QByteArray a = s.toUtf8();
|
||||
if (mode == 2) // app mode
|
||||
{
|
||||
toWrite += a.size();
|
||||
sasl->write(a); // write to sasl
|
||||
} else // mech list or sasl negotiation
|
||||
sock->write(a); // write to socket
|
||||
}
|
||||
};
|
||||
|
||||
// --- ServerTest implementation
|
||||
|
||||
ServerTest::ServerTest(const QString &_host,
|
||||
int _port,
|
||||
const QString &_proto,
|
||||
const QString &_realm,
|
||||
const QString &_str)
|
||||
: host(_host)
|
||||
, proto(_proto)
|
||||
, realm(_realm)
|
||||
, str(_str)
|
||||
, port(_port)
|
||||
{
|
||||
tcpServer = new QTcpServer(this);
|
||||
connect(tcpServer, &QTcpServer::newConnection, this, &ServerTest::server_newConnection);
|
||||
}
|
||||
|
||||
int ServerTest::reserveId()
|
||||
{
|
||||
int n = 0;
|
||||
while (ids.contains(n))
|
||||
++n;
|
||||
ids += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
void ServerTest::releaseId(int id)
|
||||
{
|
||||
ids.removeAll(id);
|
||||
}
|
||||
|
||||
void ServerTest::start()
|
||||
{
|
||||
if (!tcpServer->listen(QHostAddress::Any, port)) {
|
||||
printf("Error: unable to bind to port %d.\n", port);
|
||||
emit quit();
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Serving on %s:%d, for protocol %s ...\n", qPrintable(host), port, qPrintable(proto));
|
||||
}
|
||||
|
||||
void ServerTest::server_newConnection()
|
||||
{
|
||||
QTcpSocket *sock = tcpServer->nextPendingConnection();
|
||||
new ServerTestHandler(this, sock, host, proto, realm, str);
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
void usage()
|
||||
{
|
||||
printf("usage: saslserver host (message)\n");
|
||||
printf("options: --proto=x, --realm=x\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCA::Initializer init;
|
||||
QCoreApplication qapp(argc, argv);
|
||||
|
||||
QCA::setAppName(QStringLiteral("saslserver"));
|
||||
|
||||
QStringList args = qapp.arguments();
|
||||
args.removeFirst();
|
||||
|
||||
// options
|
||||
QString proto = QStringLiteral("qcatest"); // default protocol
|
||||
QString realm;
|
||||
for (int n = 0; n < args.count(); ++n) {
|
||||
if (!args[n].startsWith(QLatin1String("--")))
|
||||
continue;
|
||||
|
||||
QString opt = args[n].mid(2);
|
||||
QString var, val;
|
||||
int at = opt.indexOf(QLatin1Char('='));
|
||||
if (at != -1) {
|
||||
var = opt.mid(0, at);
|
||||
val = opt.mid(at + 1);
|
||||
} else
|
||||
var = opt;
|
||||
|
||||
if (var == QLatin1String("proto"))
|
||||
proto = val;
|
||||
else if (var == QLatin1String("realm"))
|
||||
realm = val;
|
||||
|
||||
args.removeAt(n);
|
||||
--n; // adjust position
|
||||
}
|
||||
|
||||
if (args.count() < 1) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString host;
|
||||
int port = 8001; // default port
|
||||
|
||||
QString hostinput = args[0];
|
||||
QString str = QStringLiteral("Hello, World");
|
||||
if (args.count() >= 2)
|
||||
str = args[1];
|
||||
|
||||
int at = hostinput.indexOf(QLatin1Char(':'));
|
||||
if (at != -1) {
|
||||
host = hostinput.mid(0, at);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 2)
|
||||
port = QStringView(hostinput).mid(at + 1).toInt();
|
||||
#else
|
||||
port = hostinput.midRef(at + 1).toInt();
|
||||
#endif
|
||||
} else
|
||||
host = hostinput;
|
||||
|
||||
if (!QCA::isSupported("sasl")) {
|
||||
printf("Error: SASL support not found.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ServerTest server(host, port, proto, realm, str);
|
||||
QObject::connect(&server, &ServerTest::quit, &qapp, &QCoreApplication::quit);
|
||||
QTimer::singleShot(0, &server, &ServerTest::start);
|
||||
qapp.exec();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "saslserver.moc"
|
||||
@@ -0,0 +1,6 @@
|
||||
set(sslservtest_bin_SRCS sslservtest.cpp)
|
||||
|
||||
add_executable(sslservtest ${sslservtest_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(sslservtest)
|
||||
target_link_libraries(sslservtest Qt${QT_MAJOR_VERSION}::Network)
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
Copyright (C) 2003 Justin Karneges <justin@affinix.com>
|
||||
Copyright (C) 2006 Brad Hards <bradh@frogmouth.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QHostAddress>
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QTimer>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
char pemdata_cert[] =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIICeTCCAeKgAwIBAgIRAKKKnOj6Aarmwf0phApitVAwDQYJKoZIhvcNAQEFBQAw\n"
|
||||
"ODELMAkGA1UEBhMCVVMxFDASBgNVBAoTC0V4YW1wbGUgT3JnMRMwEQYDVQQDEwpF\n"
|
||||
"eGFtcGxlIENBMB4XDTA2MDMxNTA3MDU1MloXDTA3MDMxNTA3MDU1MlowOjEVMBMG\n"
|
||||
"A1UEAxMMRXhhbXBsZSBVc2VyMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBs\n"
|
||||
"ZSBPcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPkKn0FfHMvRZv+3uFcw\n"
|
||||
"VrOadJmANzLVeVW/DHZp4CXokXSksM66ZMqFuQRBk5rnIZZpZmVp1tTRDVt9sEAY\n"
|
||||
"YNa8CRM4HXkVlU0lCKdey18CSq2VuSvNtw8dDpoBmQt3nr9tePvKHnpS3nm6YjR2\n"
|
||||
"NEvIKt1P4mHzYXLmwoF24C1bAgMBAAGjgYAwfjAdBgNVHQ4EFgQUmQIdzyDaPYWF\n"
|
||||
"fPJ8PPOOm1eSsucwHwYDVR0jBBgwFoAUkCglAizTO7iqwLeaO6r/8kJuqhMwDAYD\n"
|
||||
"VR0TAQH/BAIwADAeBgNVHREEFzAVgRNleGFtcGxlQGV4YW1wbGUuY29tMA4GA1Ud\n"
|
||||
"DwEB/wQEAwIF4DANBgkqhkiG9w0BAQUFAAOBgQAuhbiUgy2a++EUccaonID7eTJZ\n"
|
||||
"F3D5qXMqUpQxlYxU8du+9AxDD7nFxTMkQC2pzfmEc1znRNmJ1ZeLRL72VYsVndcT\n"
|
||||
"psyM8ABkvPp1d2jWIyccVjGpt+/RN5IPKm/YIbtIZcywvWuXrOp1lanVmppLfPnO\n"
|
||||
"6yneBkC9iqjOv/+Q+A==\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
char pemdata_privkey[] =
|
||||
"-----BEGIN PRIVATE KEY-----\n"
|
||||
"MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPkKn0FfHMvRZv+3\n"
|
||||
"uFcwVrOadJmANzLVeVW/DHZp4CXokXSksM66ZMqFuQRBk5rnIZZpZmVp1tTRDVt9\n"
|
||||
"sEAYYNa8CRM4HXkVlU0lCKdey18CSq2VuSvNtw8dDpoBmQt3nr9tePvKHnpS3nm6\n"
|
||||
"YjR2NEvIKt1P4mHzYXLmwoF24C1bAgMBAAECgYEAyIjJHDaeVXDU42zovyxpZE4n\n"
|
||||
"PcOEryY+gdFJE8DFgUD4f1huFsj4iCuNg+PaG42p+hf9IARNvSho/RcEaVg4AJrV\n"
|
||||
"jRP8r7fSqcIGr6lGuvDFFv3SU5ddy84g5oqLYGKvuPSHMGfVsZSxAwOrzD4bH19L\n"
|
||||
"SNqtNcpdBsBd7ZiEE4ECQQD/oJGui9D5Dx3QVcS+QV4F8wuyN9jYIANmX/17o0fl\n"
|
||||
"BL0bwRU4RICwadrcybi5N0JQLIYSUm2HGqNvAJbtnuQxAkEA+WeYLLYPeawcy+WU\n"
|
||||
"kGcOR7BUjHiG71+6cvU4XIDW2bezA04fqWXkZRFAwHTMpQb785/XalFftgS21kql\n"
|
||||
"8yLDSwJAHkeT2hwftdDPlEUEmBDAJW5DvWmWGwu3u2G1cfbGZl9oUyhM7ixXHg57\n"
|
||||
"6VlPs0jTZxHPE86FwNIr99MXDbCbkQJBAMDFOJK+ecGirXNP1P+0GA6DFSap9inJ\n"
|
||||
"BRTbwx+EmgwX966DUOefEOSpbDIVVSPs/Qr2LgtIMEFA7Y0+j3wZD3cCQBsTwccd\n"
|
||||
"ASQx59xakpq11eOlTYz14rjwodr4QMyj26WxEPJtz7hKokx/+EH6fWuPIUSrROM5\n"
|
||||
"07y2gaVbYxtis0s=\n"
|
||||
"-----END PRIVATE KEY-----\n";
|
||||
|
||||
class SecureServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
Idle,
|
||||
Handshaking,
|
||||
Active,
|
||||
Closing
|
||||
};
|
||||
|
||||
SecureServer(quint16 _port)
|
||||
: port(_port)
|
||||
{
|
||||
server = new QTcpServer;
|
||||
connect(server, &QTcpServer::newConnection, this, &SecureServer::server_handleConnection);
|
||||
|
||||
ssl = new QCA::TLS;
|
||||
connect(ssl, &QCA::TLS::handshaken, this, &SecureServer::ssl_handshaken);
|
||||
connect(ssl, &QCA::TLS::readyRead, this, &SecureServer::ssl_readyRead);
|
||||
connect(ssl, &QCA::TLS::readyReadOutgoing, this, &SecureServer::ssl_readyReadOutgoing);
|
||||
connect(ssl, &QCA::TLS::closed, this, &SecureServer::ssl_closed);
|
||||
connect(ssl, &QCA::TLS::error, this, &SecureServer::ssl_error);
|
||||
|
||||
cert = QCA::Certificate::fromPEM(QString::fromLatin1(pemdata_cert));
|
||||
privkey = QCA::PrivateKey::fromPEM(QString::fromLatin1(pemdata_privkey));
|
||||
|
||||
mode = Idle;
|
||||
}
|
||||
|
||||
~SecureServer() override
|
||||
{
|
||||
delete ssl;
|
||||
delete server;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
if (cert.isNull()) {
|
||||
qDebug() << "Error loading cert!";
|
||||
QTimer::singleShot(0, this, &SecureServer::quit);
|
||||
return;
|
||||
}
|
||||
if (privkey.isNull()) {
|
||||
qDebug() << "Error loading private key!";
|
||||
QTimer::singleShot(0, this, &SecureServer::quit);
|
||||
return;
|
||||
}
|
||||
if (false == server->listen(QHostAddress::Any, port)) {
|
||||
qDebug() << "Error binding to port " << port;
|
||||
QTimer::singleShot(0, this, &SecureServer::quit);
|
||||
return;
|
||||
}
|
||||
qDebug() << "Listening on port" << port;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void quit();
|
||||
|
||||
private Q_SLOTS:
|
||||
void sock_readyRead()
|
||||
{
|
||||
QByteArray buf(sock->bytesAvailable(), 0x00);
|
||||
|
||||
int num = sock->read(buf.data(), buf.size());
|
||||
|
||||
if (-1 == num)
|
||||
qDebug() << "Error reading data from socket";
|
||||
|
||||
if (num < buf.size())
|
||||
buf.resize(num);
|
||||
|
||||
ssl->writeIncoming(buf);
|
||||
}
|
||||
|
||||
void server_handleConnection()
|
||||
{
|
||||
// Note: only 1 connection supported at a time in this example!
|
||||
if (mode != Idle) {
|
||||
QTcpSocket *tmp = server->nextPendingConnection();
|
||||
tmp->close();
|
||||
connect(tmp, &QTcpSocket::disconnected, tmp, &QTcpSocket::deleteLater);
|
||||
qDebug() << "throwing away extra connection";
|
||||
return;
|
||||
}
|
||||
mode = Handshaking;
|
||||
sock = server->nextPendingConnection();
|
||||
connect(sock, &QTcpSocket::readyRead, this, &SecureServer::sock_readyRead);
|
||||
connect(sock, &QTcpSocket::disconnected, this, &SecureServer::sock_disconnected);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
connect(sock, &QTcpSocket::errorOccurred, this, &SecureServer::sock_error);
|
||||
#else
|
||||
connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &SecureServer::sock_error);
|
||||
#endif
|
||||
connect(sock, &QTcpSocket::bytesWritten, this, &SecureServer::sock_bytesWritten);
|
||||
|
||||
qDebug() << "Connection received! Starting TLS handshake.";
|
||||
ssl->setCertificate(cert, privkey);
|
||||
ssl->startServer();
|
||||
}
|
||||
|
||||
void sock_disconnected()
|
||||
{
|
||||
qDebug() << "Connection closed.";
|
||||
}
|
||||
|
||||
void sock_bytesWritten(qint64 x)
|
||||
{
|
||||
if (mode == Active && sent) {
|
||||
qint64 bytes = ssl->convertBytesWritten(x);
|
||||
bytesLeft -= bytes;
|
||||
|
||||
if (bytesLeft == 0) {
|
||||
mode = Closing;
|
||||
qDebug() << "Data transfer complete - SSL shutting down";
|
||||
ssl->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sock_error(QAbstractSocket::SocketError error)
|
||||
{
|
||||
qDebug() << "Socket error: " << (unsigned)error;
|
||||
}
|
||||
|
||||
void ssl_handshaken()
|
||||
{
|
||||
qDebug() << "Successful SSL handshake. Waiting for newline.";
|
||||
bytesLeft = 0;
|
||||
sent = false;
|
||||
mode = Active;
|
||||
ssl->continueAfterStep();
|
||||
}
|
||||
|
||||
void ssl_readyRead()
|
||||
{
|
||||
ssl->read();
|
||||
QByteArray b =
|
||||
"<html>\n"
|
||||
"<head><title>Test</title></head>\n"
|
||||
"<body>this is only a test</body>\n"
|
||||
"</html>\n";
|
||||
|
||||
qDebug() << "Sending test response.";
|
||||
sent = true;
|
||||
ssl->write(b);
|
||||
}
|
||||
|
||||
void ssl_readyReadOutgoing()
|
||||
{
|
||||
int plainBytes;
|
||||
QByteArray outgoingData = ssl->readOutgoing(&plainBytes);
|
||||
sock->write(outgoingData);
|
||||
}
|
||||
|
||||
void ssl_closed()
|
||||
{
|
||||
qDebug() << "Closing socket.";
|
||||
sock->close();
|
||||
mode = Idle;
|
||||
}
|
||||
|
||||
void ssl_error()
|
||||
{
|
||||
if (ssl->errorCode() == QCA::TLS::ErrorHandshake) {
|
||||
qDebug() << "SSL Handshake Error! Closing.";
|
||||
sock->close();
|
||||
} else {
|
||||
qDebug() << "SSL Error! Closing.";
|
||||
sock->close();
|
||||
}
|
||||
mode = Idle;
|
||||
}
|
||||
|
||||
private:
|
||||
quint16 port;
|
||||
QTcpServer *server;
|
||||
QTcpSocket *sock;
|
||||
QCA::TLS *ssl;
|
||||
QCA::Certificate cert;
|
||||
QCA::PrivateKey privkey;
|
||||
|
||||
bool sent;
|
||||
int mode;
|
||||
qint64 bytesLeft;
|
||||
};
|
||||
|
||||
#include "sslservtest.moc"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
int port = argc > 1 ? QString::fromLatin1(argv[1]).toInt() : 8000;
|
||||
|
||||
if (!QCA::isSupported("tls")) {
|
||||
qDebug() << "TLS not supported!";
|
||||
return 1;
|
||||
}
|
||||
|
||||
SecureServer *server = new SecureServer(port);
|
||||
QObject::connect(server, &SecureServer::quit, &app, &QCoreApplication::quit);
|
||||
server->start();
|
||||
app.exec();
|
||||
delete server;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
set(ssltest_bin_SRCS ssltest.cpp)
|
||||
|
||||
add_executable(ssltest ${ssltest_bin_SRCS})
|
||||
|
||||
target_link_qca_libraries(ssltest)
|
||||
target_link_libraries(ssltest Qt${QT_MAJOR_VERSION}::Network)
|
||||
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
Copyright (C) 2003-2005 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QtCrypto>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTcpSocket>
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
char exampleCA_cert[] =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIICSzCCAbSgAwIBAgIBADANBgkqhkiG9w0BAQUFADA4MRMwEQYDVQQDEwpFeGFt\n"
|
||||
"cGxlIENBMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBsZSBPcmcwHhcNMDYw\n"
|
||||
"MzE1MDY1ODMyWhcNMDYwNDE1MDY1ODMyWjA4MRMwEQYDVQQDEwpFeGFtcGxlIENB\n"
|
||||
"MQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBsZSBPcmcwgZ8wDQYJKoZIhvcN\n"
|
||||
"AQEBBQADgY0AMIGJAoGBAL6ULdOxmpeZ+G/ypV12eNO4qnHSVIPTrYPkQuweXqPy\n"
|
||||
"atwGFheG+hLVsNIh9GGOS0tCe7a3hBBKN0BJg1ppfk2x39cDx7hefYqjBuZvp/0O\n"
|
||||
"8Ja3qlQiJLezITZKLxMBrsibcvcuH8zpfUdys2yaN+YGeqNfjQuoNN3Byl1TwuGJ\n"
|
||||
"AgMBAAGjZTBjMB0GA1UdDgQWBBSQKCUCLNM7uKrAt5o7qv/yQm6qEzASBgNVHRMB\n"
|
||||
"Af8ECDAGAQEBAgEIMB4GA1UdEQQXMBWBE2V4YW1wbGVAZXhhbXBsZS5jb20wDgYD\n"
|
||||
"VR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4GBAAh+SIeT1Ao5qInw8oMSoTdO\n"
|
||||
"lQ6h67ec/Jk5KmK4OoskuimmHI0Sp0C5kOCLehXbsVWW8pXsNC2fv0d2HkdaSUcX\n"
|
||||
"hwLzqgyZXd4mupIYlaOTZhuHDwWPCAOZS4LVsi2tndTRHKCP12441JjNKhmZRhkR\n"
|
||||
"u5zzD60nWgM9dKTaxuZM\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
void showCertInfo(const QCA::Certificate &cert)
|
||||
{
|
||||
printf("-- Cert --\n");
|
||||
printf(" CN: %s\n", qPrintable(cert.commonName()));
|
||||
printf(" Valid from: %s, until %s\n",
|
||||
qPrintable(cert.notValidBefore().toString()),
|
||||
qPrintable(cert.notValidAfter().toString()));
|
||||
printf(" PEM:\n%s\n", qPrintable(cert.toPEM()));
|
||||
}
|
||||
|
||||
static QString validityToString(QCA::Validity v)
|
||||
{
|
||||
QString s;
|
||||
switch (v) {
|
||||
case QCA::ValidityGood:
|
||||
s = QStringLiteral("Validated");
|
||||
break;
|
||||
case QCA::ErrorRejected:
|
||||
s = QStringLiteral("Root CA is marked to reject the specified purpose");
|
||||
break;
|
||||
case QCA::ErrorUntrusted:
|
||||
s = QStringLiteral("Certificate not trusted for the required purpose");
|
||||
break;
|
||||
case QCA::ErrorSignatureFailed:
|
||||
s = QStringLiteral("Invalid signature");
|
||||
break;
|
||||
case QCA::ErrorInvalidCA:
|
||||
s = QStringLiteral("Invalid CA certificate");
|
||||
break;
|
||||
case QCA::ErrorInvalidPurpose:
|
||||
s = QStringLiteral("Invalid certificate purpose");
|
||||
break;
|
||||
case QCA::ErrorSelfSigned:
|
||||
s = QStringLiteral("Certificate is self-signed");
|
||||
break;
|
||||
case QCA::ErrorRevoked:
|
||||
s = QStringLiteral("Certificate has been revoked");
|
||||
break;
|
||||
case QCA::ErrorPathLengthExceeded:
|
||||
s = QStringLiteral("Maximum certificate chain length exceeded");
|
||||
break;
|
||||
case QCA::ErrorExpired:
|
||||
s = QStringLiteral("Certificate has expired");
|
||||
break;
|
||||
case QCA::ErrorExpiredCA:
|
||||
s = QStringLiteral("CA has expired");
|
||||
break;
|
||||
case QCA::ErrorValidityUnknown:
|
||||
default:
|
||||
s = QStringLiteral("General certificate validation error");
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
class SecureTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SecureTest()
|
||||
{
|
||||
sock_done = false;
|
||||
ssl_done = false;
|
||||
|
||||
sock = new QTcpSocket;
|
||||
connect(sock, &QTcpSocket::connected, this, &SecureTest::sock_connected);
|
||||
connect(sock, &QTcpSocket::readyRead, this, &SecureTest::sock_readyRead);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
connect(sock, &QTcpSocket::errorOccurred, this, &SecureTest::sock_error);
|
||||
#else
|
||||
connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &SecureTest::sock_error);
|
||||
#endif
|
||||
|
||||
ssl = new QCA::TLS;
|
||||
connect(ssl, &QCA::TLS::certificateRequested, this, &SecureTest::ssl_certificateRequested);
|
||||
connect(ssl, &QCA::TLS::handshaken, this, &SecureTest::ssl_handshaken);
|
||||
connect(ssl, &QCA::TLS::readyRead, this, &SecureTest::ssl_readyRead);
|
||||
connect(ssl, &QCA::TLS::readyReadOutgoing, this, &SecureTest::ssl_readyReadOutgoing);
|
||||
connect(ssl, &QCA::TLS::closed, this, &SecureTest::ssl_closed);
|
||||
connect(ssl, &QCA::TLS::error, this, &SecureTest::ssl_error);
|
||||
}
|
||||
|
||||
~SecureTest() override
|
||||
{
|
||||
delete ssl;
|
||||
delete sock;
|
||||
}
|
||||
|
||||
void start(const QString &_host)
|
||||
{
|
||||
int n = _host.indexOf(QLatin1Char(':'));
|
||||
int port;
|
||||
if (n != -1) {
|
||||
host = _host.mid(0, n);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
port = QStringView(_host).mid(n + 1).toInt();
|
||||
#else
|
||||
port = _host.midRef(n + 1).toInt();
|
||||
#endif
|
||||
} else {
|
||||
host = _host;
|
||||
port = 443;
|
||||
}
|
||||
|
||||
printf("Trying %s:%d...\n", qPrintable(host), port);
|
||||
sock->connectToHost(host, port);
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void quit();
|
||||
|
||||
private Q_SLOTS:
|
||||
void sock_connected()
|
||||
{
|
||||
// We just do this to help doxygen...
|
||||
QCA::TLS *ssl = SecureTest::ssl;
|
||||
|
||||
printf("Connected, starting TLS handshake...\n");
|
||||
|
||||
QCA::CertificateCollection rootCerts = QCA::systemStore();
|
||||
|
||||
// We add this one to show how, and to make it work with
|
||||
// the server example.
|
||||
rootCerts.addCertificate(QCA::Certificate::fromPEM(QString::fromLatin1(exampleCA_cert)));
|
||||
|
||||
if (!QCA::haveSystemStore())
|
||||
printf("Warning: no root certs\n");
|
||||
else
|
||||
ssl->setTrustedCertificates(rootCerts);
|
||||
|
||||
ssl->startClient(host);
|
||||
}
|
||||
|
||||
void sock_readyRead()
|
||||
{
|
||||
// We just do this to help doxygen...
|
||||
QCA::TLS *ssl = SecureTest::ssl;
|
||||
|
||||
ssl->writeIncoming(sock->readAll());
|
||||
}
|
||||
|
||||
void sock_connectionClosed()
|
||||
{
|
||||
printf("\nConnection closed.\n");
|
||||
sock_done = true;
|
||||
|
||||
if (ssl_done && sock_done)
|
||||
emit quit();
|
||||
}
|
||||
|
||||
void sock_error(QAbstractSocket::SocketError x)
|
||||
{
|
||||
if (x == QAbstractSocket::RemoteHostClosedError) {
|
||||
sock_connectionClosed();
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\nSocket error.\n");
|
||||
emit quit();
|
||||
}
|
||||
|
||||
void ssl_handshaken()
|
||||
{
|
||||
// We just do this to help doxygen...
|
||||
QCA::TLS *ssl = SecureTest::ssl;
|
||||
|
||||
QCA::TLS::IdentityResult r = ssl->peerIdentityResult();
|
||||
|
||||
printf("Successful SSL handshake using %s (%i of %i bits)\n",
|
||||
qPrintable(ssl->cipherSuite()),
|
||||
ssl->cipherBits(),
|
||||
ssl->cipherMaxBits());
|
||||
if (r != QCA::TLS::NoCertificate) {
|
||||
cert = ssl->peerCertificateChain().primary();
|
||||
if (!cert.isNull())
|
||||
showCertInfo(cert);
|
||||
}
|
||||
|
||||
QString str = QStringLiteral("Peer Identity: ");
|
||||
if (r == QCA::TLS::Valid)
|
||||
str += QStringLiteral("Valid");
|
||||
else if (r == QCA::TLS::HostMismatch)
|
||||
str += QStringLiteral("Error: Wrong certificate");
|
||||
else if (r == QCA::TLS::InvalidCertificate)
|
||||
str += QStringLiteral("Error: Invalid certificate.\n -> Reason: ") +
|
||||
validityToString(ssl->peerCertificateValidity());
|
||||
else
|
||||
str += QStringLiteral("Error: No certificate");
|
||||
printf("%s\n", qPrintable(str));
|
||||
|
||||
ssl->continueAfterStep();
|
||||
|
||||
printf("Let's try a GET request now.\n");
|
||||
QString req = QStringLiteral("GET / HTTP/1.0\nHost: ") + host + QStringLiteral("\n\n");
|
||||
ssl->write(req.toLatin1());
|
||||
}
|
||||
|
||||
void ssl_certificateRequested()
|
||||
{
|
||||
// We just do this to help doxygen...
|
||||
QCA::TLS *ssl = SecureTest::ssl;
|
||||
|
||||
printf("Server requested client certificate.\n");
|
||||
QList<QCA::CertificateInfoOrdered> issuerList = ssl->issuerList();
|
||||
if (!issuerList.isEmpty()) {
|
||||
printf("Allowed issuers:\n");
|
||||
foreach (QCA::CertificateInfoOrdered i, issuerList)
|
||||
printf(" %s\n", qPrintable(i.toString()));
|
||||
}
|
||||
|
||||
ssl->continueAfterStep();
|
||||
}
|
||||
|
||||
void ssl_readyRead()
|
||||
{
|
||||
// We just do this to help doxygen...
|
||||
QCA::TLS *ssl = SecureTest::ssl;
|
||||
|
||||
QByteArray a = ssl->read();
|
||||
printf("%s", a.data());
|
||||
}
|
||||
|
||||
void ssl_readyReadOutgoing()
|
||||
{
|
||||
// We just do this to help doxygen...
|
||||
QCA::TLS *ssl = SecureTest::ssl;
|
||||
|
||||
sock->write(ssl->readOutgoing());
|
||||
}
|
||||
|
||||
void ssl_closed()
|
||||
{
|
||||
printf("SSL session closed.\n");
|
||||
ssl_done = true;
|
||||
|
||||
if (ssl_done && sock_done)
|
||||
emit quit();
|
||||
}
|
||||
|
||||
void ssl_error()
|
||||
{
|
||||
// We just do this to help doxygen...
|
||||
QCA::TLS *ssl = SecureTest::ssl;
|
||||
|
||||
int x = ssl->errorCode();
|
||||
if (x == QCA::TLS::ErrorHandshake) {
|
||||
printf("SSL Handshake Error!\n");
|
||||
emit quit();
|
||||
} else {
|
||||
printf("SSL Error!\n");
|
||||
emit quit();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QString host;
|
||||
QTcpSocket *sock;
|
||||
QCA::TLS *ssl;
|
||||
QCA::Certificate cert;
|
||||
bool sock_done, ssl_done;
|
||||
};
|
||||
|
||||
#include "ssltest.moc"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCA::Initializer init;
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
QString host = argc > 1 ? QString::fromLocal8Bit(argv[1]) : QStringLiteral("andbit.net");
|
||||
|
||||
if (!QCA::isSupported("tls")) {
|
||||
printf("TLS not supported!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
SecureTest *s = new SecureTest;
|
||||
QObject::connect(s, &SecureTest::quit, &app, &QCoreApplication::quit);
|
||||
s->start(host);
|
||||
app.exec();
|
||||
delete s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
add_executable(tlssocket tlssocket.cpp main.cpp)
|
||||
|
||||
target_link_qca_libraries(tlssocket)
|
||||
target_link_libraries(tlssocket Qt${QT_MAJOR_VERSION}::Network)
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tlssocket.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCA::Initializer init;
|
||||
QCoreApplication qapp(argc, argv);
|
||||
|
||||
TLSSocket socket;
|
||||
socket.connectToHostEncrypted(QStringLiteral("www.paypal.com"), 443);
|
||||
socket.write("GET / HTTP/1.0\r\n\r\n");
|
||||
while (socket.waitForReadyRead())
|
||||
printf("%s", socket.readAll().constData());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tlssocket.h"
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
#include "import_plugins.h"
|
||||
#endif
|
||||
|
||||
class TLSSocket::Private : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TLSSocket *q;
|
||||
QTcpSocket *sock;
|
||||
QCA::TLS *tls;
|
||||
QString host;
|
||||
bool encrypted;
|
||||
bool error, done;
|
||||
QByteArray readbuf, writebuf;
|
||||
QCA::Synchronizer sync;
|
||||
bool waiting;
|
||||
|
||||
Private(TLSSocket *_q)
|
||||
: QObject(_q)
|
||||
, q(_q)
|
||||
, sync(_q)
|
||||
{
|
||||
sock = new QTcpSocket(this);
|
||||
connect(sock, &QTcpSocket::connected, this, &TLSSocket::Private::sock_connected);
|
||||
connect(sock, &QTcpSocket::readyRead, this, &TLSSocket::Private::sock_readyRead);
|
||||
connect(sock, &QTcpSocket::bytesWritten, this, &TLSSocket::Private::sock_bytesWritten);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
connect(sock, &QTcpSocket::errorOccurred, this, &TLSSocket::Private::sock_error);
|
||||
#else
|
||||
connect(sock,
|
||||
QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error),
|
||||
this,
|
||||
&TLSSocket::Private::sock_error);
|
||||
#endif
|
||||
|
||||
tls = new QCA::TLS(this);
|
||||
connect(tls, &QCA::TLS::handshaken, this, &TLSSocket::Private::tls_handshaken);
|
||||
connect(tls, &QCA::TLS::readyRead, this, &TLSSocket::Private::tls_readyRead);
|
||||
connect(tls, &QCA::TLS::readyReadOutgoing, this, &TLSSocket::Private::tls_readyReadOutgoing);
|
||||
connect(tls, &QCA::TLS::closed, this, &TLSSocket::Private::tls_closed);
|
||||
connect(tls, &QCA::TLS::error, this, &TLSSocket::Private::tls_error);
|
||||
tls->setTrustedCertificates(QCA::systemStore());
|
||||
encrypted = false;
|
||||
error = false;
|
||||
waiting = false;
|
||||
done = false;
|
||||
}
|
||||
|
||||
bool waitForReadyRead(int msecs)
|
||||
{
|
||||
waiting = true;
|
||||
bool ok = sync.waitForCondition(msecs);
|
||||
// while(1)
|
||||
// QCoreApplication::instance()->processEvents();
|
||||
waiting = false;
|
||||
if (error || done)
|
||||
return false;
|
||||
return ok;
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void sock_connected()
|
||||
{
|
||||
// printf("sock connected\n");
|
||||
tls->startClient(host);
|
||||
}
|
||||
|
||||
void sock_readyRead()
|
||||
{
|
||||
// printf("sock ready read\n");
|
||||
QByteArray buf = sock->readAll();
|
||||
// printf("%d bytes\n", buf.size());
|
||||
tls->writeIncoming(buf);
|
||||
}
|
||||
|
||||
void sock_bytesWritten(qint64 x)
|
||||
{
|
||||
Q_UNUSED(x);
|
||||
// printf("sock bytes written: %d\n", (int)x);
|
||||
}
|
||||
|
||||
void sock_error(QAbstractSocket::SocketError x)
|
||||
{
|
||||
// printf("sock error: %d\n", x);
|
||||
Q_UNUSED(x);
|
||||
done = true;
|
||||
if (waiting)
|
||||
sync.conditionMet();
|
||||
}
|
||||
|
||||
void tls_handshaken()
|
||||
{
|
||||
// printf("tls handshaken\n");
|
||||
if (tls->peerIdentityResult() != QCA::TLS::Valid) {
|
||||
printf("not valid\n");
|
||||
sock->abort();
|
||||
tls->reset();
|
||||
error = true;
|
||||
} else {
|
||||
// printf("valid\n");
|
||||
encrypted = true;
|
||||
// printf("%d bytes in writebuf\n", writebuf.size());
|
||||
if (!writebuf.isEmpty()) {
|
||||
// printf("[%s]\n", writebuf.data());
|
||||
tls->write(writebuf);
|
||||
writebuf.clear();
|
||||
}
|
||||
}
|
||||
if (waiting)
|
||||
sync.conditionMet();
|
||||
}
|
||||
|
||||
void tls_readyRead()
|
||||
{
|
||||
// printf("tls ready read\n");
|
||||
if (waiting)
|
||||
sync.conditionMet();
|
||||
}
|
||||
|
||||
void tls_readyReadOutgoing()
|
||||
{
|
||||
// printf("tls ready read outgoing\n");
|
||||
QByteArray buf = tls->readOutgoing();
|
||||
// printf("%d bytes\n", buf.size());
|
||||
sock->write(buf);
|
||||
}
|
||||
|
||||
void tls_closed()
|
||||
{
|
||||
// printf("tls closed\n");
|
||||
}
|
||||
|
||||
void tls_error()
|
||||
{
|
||||
// printf("tls error\n");
|
||||
}
|
||||
};
|
||||
|
||||
TLSSocket::TLSSocket(QObject *parent)
|
||||
: QTcpSocket(parent)
|
||||
{
|
||||
d = new Private(this);
|
||||
}
|
||||
|
||||
TLSSocket::~TLSSocket()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void TLSSocket::connectToHostEncrypted(const QString &host, quint16 port)
|
||||
{
|
||||
d->host = host;
|
||||
setOpenMode(QIODevice::ReadWrite);
|
||||
d->sock->connectToHost(host, port);
|
||||
}
|
||||
|
||||
QCA::TLS *TLSSocket::tls()
|
||||
{
|
||||
return d->tls;
|
||||
}
|
||||
|
||||
bool TLSSocket::waitForReadyRead(int msecs)
|
||||
{
|
||||
/*if(d->readbuf.isEmpty())
|
||||
return false;
|
||||
|
||||
if(d->tls->bytesAvailable() == 0)
|
||||
return false;*/
|
||||
|
||||
return d->waitForReadyRead(msecs);
|
||||
}
|
||||
|
||||
qint64 TLSSocket::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
if (!d->error)
|
||||
d->readbuf += d->tls->read();
|
||||
unsigned char *p = (unsigned char *)d->readbuf.data();
|
||||
int size = d->readbuf.size();
|
||||
int readsize = qMin(size, (int)maxlen);
|
||||
int newsize = size - readsize;
|
||||
memcpy(data, p, readsize);
|
||||
memmove(p, p + readsize, newsize);
|
||||
d->readbuf.resize(newsize);
|
||||
return readsize;
|
||||
}
|
||||
|
||||
qint64 TLSSocket::writeData(const char *data, qint64 len)
|
||||
{
|
||||
// printf("write %d bytes\n", (int)len);
|
||||
QByteArray buf(data, len);
|
||||
if (d->encrypted)
|
||||
d->tls->write(buf);
|
||||
else
|
||||
d->writebuf += buf;
|
||||
return len;
|
||||
}
|
||||
|
||||
#include "tlssocket.moc"
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright (C) 2007 Justin Karneges <justin@affinix.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef TLSSOCKET_H
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <QtCrypto>
|
||||
|
||||
class TLSSocket : public QTcpSocket
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TLSSocket(QObject *parent = nullptr);
|
||||
~TLSSocket() override;
|
||||
|
||||
void connectToHostEncrypted(const QString &host, quint16 port);
|
||||
QCA::TLS *tls();
|
||||
|
||||
bool waitForReadyRead(int msecs = -1) override;
|
||||
|
||||
protected:
|
||||
// from qiodevice
|
||||
qint64 readData(char *data, qint64 maxlen) override;
|
||||
qint64 writeData(const char *data, qint64 len) override;
|
||||
|
||||
private:
|
||||
class Private;
|
||||
friend class Private;
|
||||
Private *d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
readonly output=$(git clang-format -v --diff)
|
||||
|
||||
if [[ "$output" == *"no modified files to format"* ]]; then exit 0; fi
|
||||
if [[ "$output" == *"clang-format did not modify any files"* ]]; then exit 0; fi
|
||||
|
||||
echo "ERROR: you need to run git clang-format on your commit"
|
||||
echo " git clang-format -f is potentially what you want"
|
||||
exit 1
|
||||
@@ -0,0 +1 @@
|
||||
#include "qca.h"
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* qca.h - Qt Cryptographic Architecture
|
||||
* Copyright (C) 2003-2005 Justin Karneges <justin@affinix.com>
|
||||
* Copyright (C) 2004-2006 Brad Hards <bradh@frogmouth.net>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
\file qca.h
|
||||
|
||||
Summary header file for %QCA.
|
||||
|
||||
\note You should not use this header directly from an
|
||||
application. You should just use <tt> \#include \<QtCrypto>
|
||||
</tt> instead.
|
||||
*/
|
||||
|
||||
#ifndef QCA_H
|
||||
#define QCA_H
|
||||
|
||||
#include "qca_basic.h"
|
||||
#include "qca_cert.h"
|
||||
#include "qca_core.h"
|
||||
#include "qca_keystore.h"
|
||||
#include "qca_publickey.h"
|
||||
#include "qca_safetimer.h"
|
||||
#include "qca_securelayer.h"
|
||||
#include "qca_securemessage.h"
|
||||
#include "qca_textfilter.h"
|
||||
#include "qcaprovider.h"
|
||||
#include "qpipe.h"
|
||||
|
||||
#endif
|
||||