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.
This commit is contained in:
2026-08-02 04:44:37 +03:00
parent c825f470cb
commit 7284eec11e
498 changed files with 118024 additions and 2 deletions
@@ -26,6 +26,7 @@ dependencies = [
"kf6-kwindowsystem",
"kf6-ki18n",
"libgcrypt",
"qca",
]
script = """
DYNAMIC_INIT
+1 -1
View File
@@ -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]
+1 -1
View File
@@ -45,7 +45,7 @@ typedef struct
##
EOF
cookbook_configure --enable-threads=posix
cookbook_configure --enable-threads=posix --with-pic
"""
[package]
+72
View File
@@ -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
+7
View File
@@ -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()
+504
View File
@@ -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!
File diff suppressed because it is too large Load Diff
+53
View File
@@ -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
+400
View File
@@ -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 &lt;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@)
+113
View File
@@ -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 :)
+104
View File
@@ -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
File diff suppressed because it is too large Load Diff
@@ -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
File diff suppressed because it is too large Load Diff
Binary file not shown.

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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

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>&amp;Sign</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pb_verify" >
<property name="text" >
<string>&amp;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>&amp;Help</string>
</property>
<addaction name="actionAbout" />
</widget>
<widget class="QMenu" name="menuFile" >
<property name="title" >
<string>&amp;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 &amp;File...</string>
</property>
</action>
<action name="actionLoadIdentityEntry" >
<property name="text" >
<string>Load Identity From &amp;Storage Device...</string>
</property>
</action>
<action name="actionQuit" >
<property name="text" >
<string>&amp;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 &amp;PKCS#11 Modules...</string>
</property>
</action>
<action name="actionLoadAuthority" >
<property name="text" >
<string>Load &amp;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>&amp;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&amp;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 &params)
{
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
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More