Release 2.2.2

- Fixed entries with empty URLs being reported to KeePassHTTP clients [#1031]
- Fixed YubiKey detection and enabled CLI tool for AppImage binary [#1100]
- Added AppStream description [#1082]
- Improved TOTP compatibility and added new Base32 implementation [#1069]
- Fixed error handling when processing invalid cipher stream [#1099]
- Fixed double warning display when opening a database [#1037]
- Fixed unlocking databases with --pw-stdin [#1087]
- Added ability to override QT_PLUGIN_PATH environment variable for AppImages [#1079]
- Fixed transform seed not being regenerated when saving the database [#1068]
- Fixed only one YubiKey slot being polled [#1048]
- Corrected an issue with entry icons while merging [#1008]
- Corrected desktop and tray icons in Snap package [#1030]
- Fixed screen lock and Google fallback settings [#1029]
This commit is contained in:
Janek Bevendorff 2017-10-22 00:28:08 +02:00
commit 6d46717cfc
No known key found for this signature in database
GPG Key ID: 2FDEB0D40BCA5E11
76 changed files with 1999 additions and 715 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
CMakeLists.txt.*
build*/
cmake-build-*/
release*/
.idea/
*.iml

View File

@ -34,6 +34,7 @@ fi
APP="$1"
LOWERAPP="$(echo "$APP" | tr '[:upper:]' '[:lower:]')"
VERSION="$2"
export ARCH=x86_64
mkdir -p $APP.AppDir
wget -q https://github.com/probonopd/AppImages/raw/master/functions.sh -O ./functions.sh
@ -42,6 +43,8 @@ wget -q https://github.com/probonopd/AppImages/raw/master/functions.sh -O ./func
LIB_DIR=./usr/lib
if [ -d ./usr/lib/x86_64-linux-gnu ]; then
LIB_DIR=./usr/lib/x86_64-linux-gnu
elif [ -d ./usr/lib64 ]; then
LIB_DIR=./usr/lib64
fi
cd $APP.AppDir
@ -51,7 +54,7 @@ rm -R ./usr/local
rmdir ./opt 2> /dev/null
# bundle Qt platform plugins and themes
QXCB_PLUGIN="$(find /usr/lib -name 'libqxcb.so' 2> /dev/null)"
QXCB_PLUGIN="$(find /usr/lib* -name 'libqxcb.so' 2> /dev/null)"
if [ "$QXCB_PLUGIN" == "" ]; then
QXCB_PLUGIN="$(find /opt/qt*/plugins -name 'libqxcb.so' 2> /dev/null)"
fi
@ -71,18 +74,22 @@ get_desktop
get_icon
cat << EOF > ./usr/bin/keepassxc_env
#!/usr/bin/env bash
#export QT_QPA_PLATFORMTHEME=gtk2
export LD_LIBRARY_PATH="..$(dirname ${QT_PLUGIN_PATH})/lib:\${LD_LIBRARY_PATH}"
export QT_PLUGIN_PATH="..${QT_PLUGIN_PATH}"
export QT_PLUGIN_PATH="..${QT_PLUGIN_PATH}:\${KPXC_QT_PLUGIN_PATH}"
# unset XDG_DATA_DIRS to make tray icon work in Ubuntu Unity
# see https://github.com/probonopd/AppImageKit/issues/351
# see https://github.com/AppImage/AppImageKit/issues/351
unset XDG_DATA_DIRS
exec keepassxc "\$@"
if [ "\${1}" == "cli" ]; then
shift
exec keepassxc-cli "\$@"
else
exec keepassxc "\$@"
fi
EOF
chmod +x ./usr/bin/keepassxc_env
sed -i 's/Exec=keepassxc/Exec=keepassxc_env/' keepassxc.desktop
sed -i 's/Exec=keepassxc/Exec=keepassxc_env/' org.keepassxc.desktop
get_desktopintegration $LOWERAPP
GLIBC_NEEDED=$(glibc_needed)
@ -91,5 +98,5 @@ cd ..
generate_type2_appimage
mv ../out/*.AppImage ..
mv ../out/*.AppImage ../KeePassXC-${VERSION}-${ARCH}.AppImage
rmdir ../out > /dev/null 2>&1

View File

@ -1,3 +1,20 @@
2.2.2 (2017-10-22)
=========================
- Fixed entries with empty URLs being reported to KeePassHTTP clients [#1031]
- Fixed YubiKey detection and enabled CLI tool for AppImage binary [#1100]
- Added AppStream description [#1082]
- Improved TOTP compatibility and added new Base32 implementation [#1069]
- Fixed error handling when processing invalid cipher stream [#1099]
- Fixed double warning display when opening a database [#1037]
- Fixed unlocking databases with --pw-stdin [#1087]
- Added ability to override QT_PLUGIN_PATH environment variable for AppImages [#1079]
- Fixed transform seed not being regenerated when saving the database [#1068]
- Fixed only one YubiKey slot being polled [#1048]
- Corrected an issue with entry icons while merging [#1008]
- Corrected desktop and tray icons in Snap package [#1030]
- Fixed screen lock and Google fallback settings [#1029]
2.2.1 (2017-10-01)
=========================

View File

@ -49,9 +49,21 @@ set(CMAKE_AUTOUIC ON)
set(KEEPASSXC_VERSION_MAJOR "2")
set(KEEPASSXC_VERSION_MINOR "2")
set(KEEPASSXC_VERSION_PATCH "1")
set(KEEPASSXC_VERSION_PATCH "2")
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
# Distribution info
set(KEEPASSXC_DIST True)
set(KEEPASSXC_DIST_TYPE "Other" CACHE STRING "KeePassXC Distribution type")
set_property(CACHE KEEPASSXC_DIST_TYPE PROPERTY STRINGS Snap AppImage Other)
if(KEEPASSXC_DIST_TYPE STREQUAL "Snap")
set(KEEPASSXC_DIST_SNAP True)
elseif(KEEPASSXC_DIST_TYPE STREQUAL "AppImage")
set(KEEPASSXC_DIST_APPIMAGE True)
elseif(KEEPASSXC_DIST_TYPE STREQUAL "Other")
unset(KEEPASSXC_DIST)
endif()
if("${CMAKE_C_COMPILER}" MATCHES "clang$" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_COMPILER_IS_CLANG 1)
endif()

View File

@ -214,10 +214,6 @@ Files: share/icons/database/C65_W.png
Copyright: none
License: public-domain
Files: src/crypto/salsa20/*
Copyright: none
License: public-domain
Files: src/streams/qtiocompressor.*
src/streams/QtIOCompressor
tests/modeltest.*
@ -241,8 +237,3 @@ Files: src/gui/KMessageWidget.h
Copyright: 2011 Aurélien Gâteau <agateau@kde.org>
2014 Dominik Haumann <dhaumann@kde.org>
License: LGPL-2.1
Files: src/totp/base32.cpp
src/totp/base32.h
Copyright: 2010 Google Inc.
License: Apache 2.0

View File

@ -14,49 +14,68 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
FROM ubuntu:14.04
FROM centos:7
RUN set -x \
&& apt-get update \
&& apt-get install --yes software-properties-common
&& curl "https://copr.fedorainfracloud.org/coprs/bugzy/keepassxc/repo/epel-7/bugzy-keepassxc-epel-7.repo" \
> /etc/yum.repos.d/bugzy-keepassxc-epel-7.repo
RUN set -x \
&& add-apt-repository ppa:george-edison55/cmake-3.x
ENV QT_VERSION=qt59
&& curl "https://copr.fedorainfracloud.org/coprs/sic/backports/repo/epel-7/sic-backports-epel-7.repo" \
> /etc/yum.repos.d/sic-backports-epel-7.repo
RUN set -x \
&& add-apt-repository --yes ppa:beineri/opt-${QT_VERSION}-trusty
&& yum clean -y all \
&& yum upgrade -y
# build and runtime dependencies
RUN set -x \
&& apt-get update \
&& apt-get install --yes \
g++ \
&& yum install -y \
make \
automake \
gcc-c++ \
cmake \
libgcrypt20-dev \
${QT_VERSION}base \
${QT_VERSION}tools \
${QT_VERSION}x11extras \
libxi-dev \
libxtst-dev \
zlib1g-dev \
libyubikey-dev \
libykpers-1-dev \
xvfb \
wget \
file \
fuse \
python
libgcrypt16-devel \
qt5-qtbase-devel \
qt5-linguist \
qt5-qttools \
zlib-devel \
qt5-qtx11extras \
qt5-qtx11extras-devel \
libXi-devel \
libXtst-devel
# AppImage dependencies
RUN set -x \
&& apt-get install --yes mesa-common-dev
&& yum install -y \
wget \
fuse-libs
# build libyubikey
ENV YUBIKEY_VERSION=1.13
RUN set -x && yum install -y libusb-devel
RUN set -x \
&& wget "https://developers.yubico.com/yubico-c/Releases/libyubikey-${YUBIKEY_VERSION}.tar.gz" \
&& tar xf libyubikey-${YUBIKEY_VERSION}.tar.gz \
&& cd libyubikey-${YUBIKEY_VERSION} \
&& ./configure --prefix=/usr --libdir=/usr/lib64 \
&& make \
&& make install \
&& cd .. \
&& rm -Rf libyubikey-${YUBIKEY_VERSION}*
# build libykpers-1
ENV YKPERS_VERSION=1.18.0
RUN set -x \
&& wget "https://developers.yubico.com/yubikey-personalization/Releases/ykpers-${YKPERS_VERSION}.tar.gz" \
&& tar xf ykpers-${YKPERS_VERSION}.tar.gz \
&& cd ykpers-${YKPERS_VERSION} \
&& ./configure --prefix=/usr --libdir=/usr/lib64 \
&& make \
&& make install \
&& cd .. \
&& rm -Rf ykpers-${YKPERS_VERSION}*
VOLUME /keepassxc/src
VOLUME /keepassxc/out
WORKDIR /keepassxc
ENV CMAKE_PREFIX_PATH=/opt/${QT_VERSION}/lib/cmake
ENV LD_LIBRARY_PATH=/opt/${QT_VERSION}/lib
RUN set -x \
&& echo /opt/${QT_VERSION}/lib > /etc/ld.so.conf.d/${QT_VERSION}.conf

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -14,7 +14,7 @@ KeePass Cross-platform Community Edition
- Using website favicons as entry icons
- Merging of databases
- Automatic reload when the database changed on disk
- KeePassHTTP support for use with [PassIFox](https://addons.mozilla.org/en-us/firefox/addon/passifox/) in Mozilla Firefox and [chromeIPass](https://chrome.google.com/webstore/detail/chromeipass/ompiailgknfdndiefoaoiligalphfdae) in Google Chrome or Chromium, and [passafari](https://github.com/mmichaa/passafari.safariextension/) in Safari.
- KeePassHTTP support for use with KeePassHTTP-Connector for [Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepasshttp-connector/) and [Google Chrome or Chromium](https://chrome.google.com/webstore/detail/keepasshttp-connector/dafgdjggglmmknipkhngniifhplpcldb), and [passafari](https://github.com/mmichaa/passafari.safariextension/) in Safari.
- Many bug fixes
For a full list of features and changes, read the [CHANGELOG](CHANGELOG) document.

View File

@ -119,9 +119,11 @@ EOF
Sign previously compiled release packages
Options:
-f, --files Files to sign (required)
-g, --gpg-key GPG key used to sign the files (default: '${GPG_KEY}')
-h, --help Show this help
-f, --files Files to sign (required)
-g, --gpg-key GPG key used to sign the files (default: '${GPG_KEY}')
--signtool Specify the signtool executable (default: 'signtool')
--signtool-key Provide a key to be used with signtool (for Windows EXE)
-h, --help Show this help
EOF
fi
}
@ -257,19 +259,18 @@ checkChangeLog() {
grep -qPzo "${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n=+\n" CHANGELOG
if [ $? -ne 0 ]; then
exitError "CHANGELOG does not contain any information about the '${RELEASE_NAME}' release!"
exitError "'CHANGELOG' has not been updated to the '${RELEASE_NAME}' release!"
fi
}
checkTransifexCommandExists() {
command -v tx > /dev/null
if [ 0 -ne $? ]; then
exitError "Transifex tool 'tx' not installed! Please install it using 'pip install transifex-client'"
checkAppStreamInfo() {
if [ ! -f share/linux/org.keepassxc.appdata.xml ]; then
exitError "No AppStream info file found!"
fi
command -v lupdate-qt5 > /dev/null
if [ 0 -ne $? ]; then
exitError "Qt Linguist tool (lupdate-qt5) is not installed! Please install using 'apt install qttools5-dev-tools'"
grep -qPzo "<release version=\"${RELEASE_NAME}\" date=\"\d{4}-\d{2}-\d{2}\">" share/linux/org.keepassxc.appdata.xml
if [ $? -ne 0 ]; then
exitError "'share/linux/org.keepassxc.appdata.xml' has not been updated to the '${RELEASE_NAME}' release!"
fi
}
@ -281,7 +282,24 @@ checkSnapcraft() {
grep -qPzo "version: ${RELEASE_NAME}" snapcraft.yaml
if [ $? -ne 0 ]; then
exitError "snapcraft.yaml has not been updated to the '${RELEASE_NAME}' release!"
exitError "'snapcraft.yaml' has not been updated to the '${RELEASE_NAME}' release!"
fi
}
checkTransifexCommandExists() {
command -v tx > /dev/null
if [ 0 -ne $? ]; then
exitError "Transifex tool 'tx' not installed! Please install it using 'pip install transifex-client'"
fi
}
checkQt5LUpdateExists() {
command -v lupdate > /dev/null
if [ 0 -eq $? ] && ! $(lupdate -version | grep -q "lupdate version 5\."); then
command -v lupdate-qt5 > /dev/null
if [ 0 -ne $? ]; then
exitError "Qt Linguist tool (lupdate-qt5) is not installed! Please install using 'apt install qttools5-dev-tools'"
fi
fi
}
@ -296,6 +314,7 @@ performChecks() {
logInfo "Validating toolset and repository..."
checkTransifexCommandExists
checkQt5LUpdateExists
checkGitRepository
checkReleaseDoesNotExist
checkWorkingTreeClean
@ -309,6 +328,7 @@ performChecks() {
checkVersionInCMake
checkChangeLog
checkAppStreamInfo
checkSnapcraft
logInfo "\e[1m\e[32mAll checks passed!\e[0m"
@ -528,10 +548,10 @@ build() {
checkWorkingTreeClean
OUTPUT_DIR="$(realpath "$OUTPUT_DIR")"
logInfo "Checking out release tag '${TAG_NAME}'..."
git checkout "$TAG_NAME"
logInfo "Creating output directory..."
mkdir -p "$OUTPUT_DIR"
@ -598,7 +618,8 @@ build() {
# Building on Linux without Docker container
logInfo "Configuring build..."
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off $CMAKE_OPTIONS \
-DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" "$SRC_DIR"
-DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \
-DKEEPASSXC_DIST_TYPE=AppImage "$SRC_DIR"
logInfo "Compiling sources..."
make $MAKE_OPTIONS
@ -615,7 +636,7 @@ build() {
logInfo "Launching Docker container to compile sources..."
docker run --name "$DOCKER_CONTAINER_NAME" --rm \
--cap-add SYS_ADMIN --device /dev/fuse \
--cap-add SYS_ADMIN --security-opt apparmor:unconfined --device /dev/fuse \
-e "CC=${CC}" -e "CXX=${CXX}" \
-v "$(realpath "$SRC_DIR"):/keepassxc/src:ro" \
-v "$(realpath "$OUTPUT_DIR"):/keepassxc/out:rw" \
@ -644,6 +665,8 @@ build() {
# -----------------------------------------------------------------------
sign() {
SIGN_FILES=()
SIGNTOOL="signtool"
SIGNTOOL_KEY=""
while [ $# -ge 1 ]; do
local arg="$1"
@ -657,6 +680,14 @@ sign() {
-g|--gpg-key)
GPG_KEY="$2"
shift ;;
--signtool)
SIGNTOOL="$2"
shift ;;
--signtool-key)
SIGNTOOL_KEY="$2"
shift ;;
-h|--help)
printUsage "sign"
@ -675,13 +706,30 @@ sign() {
printUsage "sign"
exit 1
fi
if [[ -n "$SIGNTOOL_KEY" && ! -f "$SIGNTOOL_KEY" ]]; then
exitError "Signtool Key was not found!"
elif [[ -f "$SIGNTOOL_KEY" && ! -x $(command -v "${SIGNTOOL}") ]]; then
exitError "signtool program not found on PATH!"
fi
for f in "${SIGN_FILES[@]}"; do
if [ ! -f "$f" ]; then
exitError "File '${f}' does not exist!"
fi
if [[ -n "$SIGNTOOL_KEY" && ${f: -4} == '.exe' ]]; then
logInfo "Signing file '${f}' using signtool...\n"
read -s -p "Signtool Key Password: " password
echo
"${SIGNTOOL}" sign -f "${SIGNTOOL_KEY}" -p ${password} -v -t http://timestamp.comodoca.com/authenticode ${f}
if [ 0 -ne $? ]; then
exitError "Signing failed!"
fi
fi
logInfo "Signing file '${f}'..."
logInfo "Signing file '${f}' using release key..."
gpg --output "${f}.sig" --armor --local-user "$GPG_KEY" --detach-sig "$f"
if [ 0 -ne $? ]; then

View File

@ -30,7 +30,8 @@ if(UNIX AND NOT APPLE)
install(DIRECTORY icons/application/ DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor
FILES_MATCHING PATTERN "application-x-keepassxc.png" PATTERN "application-x-keepassxc.svgz"
PATTERN "status" EXCLUDE PATTERN "actions" EXCLUDE PATTERN "categories" EXCLUDE)
install(FILES linux/keepassxc.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
install(FILES linux/org.keepassxc.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
install(FILES linux/org.keepassxc.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo)
install(FILES linux/keepassxc.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages)
endif(UNIX AND NOT APPLE)

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="application/x-keepass2">
<comment>KeePass 2 database</comment>
<comment>KeePass 2 Database</comment>
<glob pattern="*.kdbx"/>
<icon name="application-x-keepassxc"/>
</mime-type>

View File

@ -0,0 +1,218 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2017 KeePassXC Team <team@keepassxc.org> -->
<component type="desktop-application">
<id>org.keepassxc</id>
<name>KeePassXC</name>
<metadata_license>CC-BY-3.0</metadata_license>
<project_license>GPL-3.0+</project_license>
<icon type="stock">keepassxc</icon>
<url type="homepage">https://keepassxc.org</url>
<mimetypes>
<mimetype>application/x-keepass2</mimetype>
</mimetypes>
<summary>Community-driven port of the Windows application “KeePass Password Safe”</summary>
<description>
<p>
KeePassXC is an application for people with extremely high demands on secure
personal data management. It has a light interface, is cross-platform and
published under the terms of the GNU General Public License.
</p>
</description>
<launchable type="desktop-id">org.keepassxc.desktop</launchable>
<screenshots>
<screenshot type="default">
<image>https://keepassxc.org/images/screenshots/linux/screen_001.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_002.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_003.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_004.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_005.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_006.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_007.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_008.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_009.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_010.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_011.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_012.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_013.png</image>
</screenshot>
<screenshot>
<image>https://keepassxc.org/images/screenshots/linux/screen_014.png</image>
</screenshot>
</screenshots>
<releases>
<release version="2.2.2" date="2017-10-22">
<description>
<ul>
<li>Fixed entries with empty URLs being reported to KeePassHTTP clients [#1031]</li>
<li>Fixed YubiKey detection and enabled CLI tool for AppImage binary [#1100]</li>
<li>Added AppStream description [#1082]</li>
<li>Improved TOTP compatibility and added new Base32 implementation [#1069]</li>
<li>Fixed error handling when processing invalid cipher stream [#1099]</li>
<li>Fixed double warning display when opening a database [#1037]</li>
<li>Fixed unlocking databases with --pw-stdin [#1087]</li>
<li>Added ability to override QT_PLUGIN_PATH environment variable for AppImages [#1079]</li>
<li>Fixed transform seed not being regenerated when saving the database [#1068]</li>
<li>Fixed only one YubiKey slot being polled [#1048]</li>
<li>Corrected an issue with entry icons while merging [#1008]</li>
<li>Corrected desktop and tray icons in Snap package [#1030]</li>
<li>Fixed screen lock and Google fallback settings [#1029]</li>
</ul>
</description>
</release><release version="2.2.1" date="2017-10-01">
<description>
<ul>
<li>Corrected multiple snap issues [#934, #1011]</li>
<li>Corrected multiple custom icon issues [#708, #719, #994]</li>
<li>Corrected multiple Yubikey issues [#880]</li>
<li>Fixed single instance preventing load on occasion [#997]</li>
<li>Keep entry history when merging databases [#970]</li>
<li>Prevent data loss if passwords were mismatched [#1007]</li>
<li>Fixed crash after merge [#941]</li>
<li>Added configurable auto-type default delay [#703]</li>
<li>Unlock database dialog window comes to front [#663]</li>
<li>Translation and compiling fixes</li>
</ul>
</description>
</release>
<release version="2.2.0" date="2017-06-23">
<description>
<ul>
<li>Added YubiKey 2FA integration for unlocking databases [#127]</li>
<li>Added TOTP support [#519]</li>
<li>Added CSV import tool [#146, #490]</li>
<li>Added KeePassXC CLI tool [#254]</li>
<li>Added diceware password generator [#373]</li>
<li>Added support for entry references [#370, #378]</li>
<li>Added support for Twofish encryption [#167]</li>
<li>Enabled DEP and ASLR for in-memory protection [#371]</li>
<li>Enabled single instance mode [#510]</li>
<li>Enabled portable mode [#645]</li>
<li>Enabled database lock on screensaver and session lock [#545]</li>
<li>Redesigned welcome screen with common features and recent databases [#292]</li>
<li>Multiple updates to search behavior [#168, #213, #374, #471, #603, #654]</li>
<li>Added auto-type fields {CLEARFIELD}, {SPACE}, {{}, {}} [#267, #427, #480]</li>
<li>Fixed auto-type errors on Linux [#550]</li>
<li>Prompt user prior to executing a cmd:// URL [#235]</li>
<li>Entry attributes can be protected (hidden) [#220]</li>
<li>Added extended ascii to password generator [#538]</li>
<li>Added new database icon to toolbar [#289]</li>
<li>Added context menu entry to empty recycle bin in databases [#520]</li>
<li>Added "apply" button to entry and group edit windows [#624]</li>
<li>Added macOS tray icon and enabled minimize on close [#583]</li>
<li>Fixed issues with unclean shutdowns [#170, #580]</li>
<li>Changed keyboard shortcut to create new database to CTRL+SHIFT+N [#515]</li>
<li>Compare window title to entry URLs [#556]</li>
<li>Implemented inline error messages [#162]</li>
<li>Ignore group expansion and other minor changes when making database "dirty" [#464]</li>
<li>Updated license and copyright information on souce files [#632]</li>
<li>Added contributors list to about dialog [#629]</li>
</ul>
</description>
</release>
<release version="2.1.4" date="2017-04-09">
<description>
<ul>
<li>Bumped KeePassHTTP version to 1.8.4.2</li>
<li>KeePassHTTP confirmation window comes to foreground [#466]</li>
</ul>
</description>
</release>
<release version="2.1.3" date="2017-03-03">
<description>
<ul>
<li>Fix possible overflow in zxcvbn library [#363]</li>
<li>Revert HiDPI setting to avoid problems on laptop screens [#332]</li>
<li>Set file meta properties in Windows executable [#330]</li>
<li>Suppress error message when auto-reloading a locked database [#345]</li>
<li>Improve usability of question dialog when database is already locked by a different instance [#346]</li>
<li>Fix compiler warnings in QHttp library [#351]</li>
<li>Use unified toolbar on Mac OS X [#361]</li>
<li>Fix an issue on X11 where the main window would be raised instead of closed on Alt+F4 [#362]</li>
</ul>
</description>
</release>
<release version="2.1.2" date="2017-02-17">
<description>
<ul>
<li>Ask for save location when creating a new database [#302]</li>
<li>Remove Libmicrohttpd dependency to clean up the code and ensure better OS X compatibility [#317, #265]</li>
<li>Prevent Qt from degrading Wifi network performance on certain platforms [#318]</li>
<li>Visually refine user interface on OS X and other platforms [#299]</li>
<li>Remove unusable tray icon setting on OS X [#293]</li>
<li>Fix compositing glitches on Ubuntu and prevent flashing when minimizing to the tray at startup [#307]</li>
<li>Fix AppImage tray icon on Ubuntu [#277, #273]</li>
<li>Fix global menu disappearing after restoring KeePassXC from the tray on Ubuntu [#276]</li>
<li>Fix result order in entry search [#320]</li>
<li>Enable HiDPI scaling on supported platforms [#315]</li>
<li>Remove empty directories from installation target [#282]</li>
</ul>
</description>
</release>
<release version="2.1.1" date="2017-02-06">
<description>
<ul>
<li>Enabled HTTP plugin build; plugin is disabled by default and limited to localhost [#147]</li>
<li>Escape HTML in dialog boxes [#247]</li>
<li>Corrected crashes in favicon download and password generator [#233, #226]</li>
<li>Increase font size of password meter [#228]</li>
<li>Fixed compatibility with Qt 5.8 [#211]</li>
<li>Use consistent button heights in password generator [#229]</li>
</ul>
</description>
</release>
<release version="2.1.0" date="2017-01-22">
<description>
<ul>
<li>Show unlock dialog when using autotype on a closed database [#10, #89]</li>
<li>Show different tray icon when database is locked [#37, #46]</li>
<li>Support autotype on Windows and OS X [#42, #60, #63]</li>
<li>Add delay feature to autotype [#76, #77]</li>
<li>Add password strength meter [#84, #92]</li>
<li>Add option for automatically locking the database when minimizing the window [#57]</li>
<li>Add feature to download favicons and use them as entry icons [#30]</li>
<li>Automatically reload and merge database when the file changed on disk [#22, #33, #93]</li>
<li>Add tool for merging two databases [#22, #47, #143]</li>
<li>Add --pw-stdin commandline option to unlock the database by providing a password on STDIN [#54]</li>
<li>Add utility script for reading the database password from KWallet [#55]</li>
<li>Fix some KeePassHTTP settings not being remembered [#34, #65]</li>
<li>Make search box persistent [#15, #67, #157]</li>
<li>Enhance search feature by scoping the search to selected group [#16, #118]</li>
<li>Improve interaction between search field and entry list [#131, #141]</li>
<li>Add stand-alone password-generator [#18, #92]</li>
<li>Don't require password repetition when password is visible [#27, #92]</li>
<li>Add support for entry attributes in autotype sequences [#107]</li>
<li>Always focus password field when opening the database unlock widget [#116, #117]</li>
<li>Fix compilation errors on various platforms [#53, #126, #130]</li>
<li>Restructure and improve kdbx-extract utility [#160]</li>
</ul>
</description>
</release>
</releases>
</component>

View File

@ -1,13 +1,16 @@
[Desktop Entry]
Name=KeePassXC
GenericName=Community Password Manager
GenericName=Password Manager
GenericName[de]=Passwortverwaltung
GenericName[es]=Gestor de contraseñas
GenericName[fr]=Gestionnaire de mot de passe
GenericName[ru]=менеджер паролей
Comment=Community-driven port of the Windows application KeePass Password Safe
Exec=keepassxc %f
TryExec=keepassxc
Icon=keepassxc
Terminal=false
Type=Application
Categories=Qt;Utility;
Version=1.0
Categories=Utility;Security;Qt;
MimeType=application/x-keepass2;

View File

@ -67,6 +67,10 @@ Jádro systému: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>K hlášení chyby vždy připojte následující údaje:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Distribuce: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1095,7 +1099,7 @@ Chcete ji přesto otevřít?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Tato vlastní ikona existuje</translation>
</message>
</context>
<context>
@ -1318,6 +1322,17 @@ Můžete ho importovat pomocí Databáze → Importovat databázi ve formátu Ke
Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otevřít ve staré verzi KeePassX 0.4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Nedaří se vyvolat výzva-odpověď.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Nedaří se spočítat hlavní klíč</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@ -1338,7 +1353,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>Existující uzamykací soubor, zajišťující spuštění pouze jedné instance, není platný. Spouští se nová instance.</translation>
</message>
</context>
<context>

View File

@ -67,6 +67,10 @@ Kerne: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Inkludér følgende information når du indrapporterer en fejl:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1313,6 +1317,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Kan ikke beregne hovednøgle</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Geben Sie folgende Informationen an, wenn Sie einen Bug melden:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Distribution: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1092,7 +1096,7 @@ Möchten Sie diese dennoch öffnen?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Es gibt bereits ein eigenes Symbol </translation>
</message>
</context>
<context>
@ -1315,6 +1319,17 @@ Zum Importieren gehen Sie auf Datenbank &gt; &apos;KeePass 1-Datenbank importier
Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann später nicht mehr mit der alten KeePassX-Version 0.4 geöffnet werden.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Fehler beim Ausführen des Challenge-Response-Verfahrens</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Berechnung des Hauptschlüssels gescheitert</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@ -1335,7 +1350,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>Vorhandene einmal-Sperrdatei ist ungültig. Starte neuen Vorgang. </translation>
</message>
</context>
<context>

View File

@ -67,6 +67,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation>Συμπεριλάβετε τις ακόλουθες πληροφορίες όποτε αναφέρετε κάποιο σφάλμα:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1311,6 +1315,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Σε θέση να υπολογίσει το κύριο κλειδί</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -66,6 +66,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1309,6 +1313,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ Núcleo: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Incluya la información siguiente cuando informe sobre un error:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1317,6 +1321,17 @@ Puede importarla haciendo clic en Base de datos &gt; &apos;Importar base de dato
Esta migración es en único sentido. No podrá abrir la base de datos importada con la vieja versión 0.4 de KeePassX.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>No se pudo hacer el desafío/respuesta:</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>No se puede calcular la clave maestra</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -65,6 +65,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"/>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1300,6 +1304,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Ezin izan da gako nagusia kalkulatu</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Sisällytä seuraavat tiedot aina kun ilmoitat ongelmasta:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1314,6 +1318,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Pääavaimen laskeminen ei onnistu</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Inclure l&apos;information suivante lorsque vous signaler un bug:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1318,6 +1322,17 @@ Vous pouvez l&apos;importer en cliquant sur Base de données&gt;&apos;Importer u
Il s&apos;agit d&apos;une migration à sens unique. Vous ne pourrez pas ouvrir la base de données importée avec l&apos;ancienne version de KeePassX 0.4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Impossible de lancer une challenge-réponse.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Impossible de calculer la clé maître</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Minden hibajelentésnél legyenek mellékelve ezek az információk:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Disztribúció: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1094,7 +1098,7 @@ Mindenképp megnyitja?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Az egyéni ikon már létezik</translation>
</message>
</context>
<context>
@ -1317,6 +1321,17 @@ Be lehet importálni az Adatbázis &gt; „KeePass 1 adatbázis importálása…
Ez egyirányú migráció. Nem lehet majd megnyitni az importált adatbázist a régi KeePassX 0.4 verzióval.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Nem lehet kiutalni a kihívás-választ.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Nem lehet kiszámítani a mesterkulcsot</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@ -1337,7 +1352,7 @@ Ez egyirányú migráció. Nem lehet majd megnyitni az importált adatbázist a
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>A meglévő egypéldányos zárolási fájl érvénytelen. Új példány indítása.</translation>
</message>
</context>
<context>

View File

@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Sertakan informasi berikut setiap Anda melaporkan bug:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1317,6 +1321,17 @@ Anda bisa mengimpornya dengan mengklik Basis Data &gt; &apos;Impor basis data Ke
Ini adalah migrasi satu arah. Anda tidak akan bisa membuka basis data yang diimpor dengan versi lama KeePassX 0.4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Tidak bisa mengkalkulasi kunci utama</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

File diff suppressed because it is too large Load Diff

View File

@ -67,6 +67,10 @@ CPU アーキテクチャ: %2
<source>Include the following information whenever you report a bug:</source>
<translation>:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1094,7 +1098,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation></translation>
</message>
</context>
<context>
@ -1317,6 +1321,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
KeePassX 0.4 </translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation></translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation></translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@ -1337,7 +1352,7 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation></translation>
</message>
</context>
<context>

View File

@ -64,6 +64,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"/>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1307,6 +1311,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Басты парольді есептеу мүмкін емес</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ CPU 아키텍처: %2
<source>Include the following information whenever you report a bug:</source>
<translation> :</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1315,6 +1319,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
. KeePassX 0.4 .</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation> .</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation> </translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ Branduolys: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Pranešdami apie klaidą, visuomet pateikite ir š informaciją:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Platinimas: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1094,7 +1098,7 @@ Ar vis tiek norite ją atverti?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Tinkinta piktograma jau yra</translation>
</message>
</context>
<context>
@ -1317,6 +1321,17 @@ Jūs galite ją importuoti, nuspausdami Duomenų bazė &gt; &quot;Importuoti Kee
Tai yra vienakryptis perkėlimas. Jūs negalėsite atverti importuotos duomenų bazės, naudodami senąją KeePassX 0.4 versija.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Nepavyko išduoti iššūkio atsakymo.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Nepavyko apskaičiuoti pagrindinio rakto</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@ -1337,7 +1352,7 @@ Tai yra vienakryptis perkėlimas. Jūs negalėsite atverti importuotos duomenų
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>Esamas vieno egzemplioriaus užrakto failas yra neteisingas. Paleidžiamas naujas egzempliorius.</translation>
</message>
</context>
<context>

View File

@ -64,6 +64,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"/>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1310,6 +1314,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Niet mogelijk om hoofdsleutel te berekenen</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ Jądro: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Uwzględnij następujące informacje, gdy zgłaszasz błąd:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Dystrybucja: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1095,7 +1099,7 @@ Czy chcesz ją otworzyć mimo to?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation>Ikona niestandardowa już istnieje</translation>
</message>
</context>
<context>
@ -1318,6 +1322,17 @@ Możesz zaimportować ją przez wybranie Baza danych &gt; &apos;Importuj bazę d
Jest to migracja w jedną stronę. Nie będzie można otworzyć importowanej bazy danych za pomocą starej wersji KeePassX 0.4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Nie można wywołać wyzwania-odpowiedzi.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Nie mogę wyliczyć głównego klucza</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@ -1338,7 +1353,7 @@ Jest to migracja w jedną stronę. Nie będzie można otworzyć importowanej baz
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>Istniejący plik blokady pojedynczego wystąpienia jest nieprawidłowy. Uruchamianie nowego wystąpienia.</translation>
</message>
</context>
<context>

View File

@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Inclua as informações abaixo quando reportar um erro:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1314,6 +1318,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Não foi possível calcular a chave mestre</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ Kernel: %3 %4</translation>
<source>Include the following information whenever you report a bug:</source>
<translation>Inclua as seguintes informações sempre que reportar um erro:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation>Distribuição: %1</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1095,7 +1099,7 @@ Ainda assim deseja abrir a base de dados?</translation>
</message>
<message>
<source>Custom icon already exists</source>
<translation type="unfinished"/>
<translation> existe um ícone personalizado</translation>
</message>
</context>
<context>
@ -1318,6 +1322,17 @@ Pode importá-lo clicando em Base de dados - &gt; &apos;Importar base de dados d
Esta é uma migração unidirecional. Não será possível abrir a base de dados importada com a versão 0.4 do KeePassX.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Incapaz de emitir a pergunta de segurança.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Impossível de calcular a chave-mestre</translation>
</message>
</context>
<context>
<name>Main</name>
<message>
@ -1338,7 +1353,7 @@ Esta é uma migração unidirecional. Não será possível abrir a base de dados
</message>
<message>
<source>Existing single-instance lock file is invalid. Launching new instance.</source>
<translation type="unfinished"/>
<translation>O ficheiro de bloqueio da instância única é inválido. A iniciar nova instância.</translation>
</message>
</context>
<context>

View File

@ -67,6 +67,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation>Включите следующую информацию, когда сообщаете об ошибке:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1318,6 +1322,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
Это одностороннее перемещение. Вы не сможете открыть импортированную базу данных на старой версии KeePassX 0,4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Не удалось выполнить запрос ответа.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Невозможно вычислить мастер-пароль</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -64,6 +64,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"/>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1305,6 +1309,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Izračun glavnega ključa ni uspel</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -24,7 +24,8 @@
<message>
<source>Version %1
</source>
<translation type="unfinished"/>
<translation>Version %1
</translation>
</message>
<message>
<source>Revision: %1</source>
@ -64,6 +65,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation type="unfinished"/>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -196,7 +201,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source>Empty password</source>
<translation type="unfinished"/>
<translation>Tomt lösenord</translation>
</message>
<message>
<source>Changing master key failed: no YubiKey inserted.</source>
@ -254,7 +259,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source>Comments start with</source>
<translation type="unfinished"/>
<translation>Kommentarer inleds med</translation>
</message>
<message>
<source>First record has field names</source>
@ -282,7 +287,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source>Empty fieldname </source>
<translation type="unfinished"/>
<translation>Tomt fältnamn </translation>
</message>
<message>
<source>column </source>
@ -337,7 +342,7 @@ Please select whether you want to allow access.</source>
</message>
<message>
<source> rows, </source>
<translation type="unfinished"/>
<translation> rader, </translation>
</message>
<message>
<source> columns</source>
@ -622,7 +627,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Open CSV file</source>
<translation type="unfinished"/>
<translation>Öppna CSV fil</translation>
</message>
</context>
<context>
@ -677,7 +682,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Searching...</source>
<translation type="unfinished"/>
<translation>Söker...</translation>
</message>
<message>
<source>No current database.</source>
@ -689,11 +694,11 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Search Results (%1)</source>
<translation type="unfinished"/>
<translation>Sökresultat (%1)</translation>
</message>
<message>
<source>No Results</source>
<translation type="unfinished"/>
<translation>Inget resultat</translation>
</message>
<message>
<source>Execute command?</source>
@ -818,7 +823,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Confirm Remove</source>
<translation type="unfinished"/>
<translation>Bekräfta borttagning</translation>
</message>
<message>
<source>Are you sure you want to remove this attribute?</source>
@ -865,7 +870,7 @@ Do you want to open it anyway?</source>
</message>
<message>
<source>Protect</source>
<translation type="unfinished"/>
<translation>Skydda</translation>
</message>
<message>
<source>Reveal</source>
@ -1307,6 +1312,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<translation type="unfinished"/>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Kunde inte räkna nu master-nyckeln</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ MİB mimarisi: %2
<source>Include the following information whenever you report a bug:</source>
<translation>Bir hata bildirirken şu bilgileri ekleyin:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1313,6 +1317,17 @@ Veri tabanı &gt; &apos;KeePass1 veri tabanı içe aktar...&apos;a tıklayarak i
Bu tek yönlü bir yer değiştirmedir. İçe aktarılan veri tabanını eski KeePassX 0.4 sürümüyle açamayacaksınız.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Ana anahtar hesaplanamıyor</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation>Коли Ви повідомляєте про ваду, завжди долучайте таку інформацію:</translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1318,6 +1322,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
Перетворення можливе лише в одному напрямку. Ви не зможете відкрити імпортоване сховище старою версією KeePassX 0.4.</translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation>Неможливо видати виклик-відповідь.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation>Неможливо вирахувати головний ключ</translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ CPU 架构:%2
<source>Include the following information whenever you report a bug:</source>
<translation> bug </translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1316,6 +1320,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
KeePassX 0.4 </translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation></translation>
</message>
<message>
<source>Unable to calculate master key</source>
<translation></translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -67,6 +67,10 @@ Kernel: %3 %4</source>
<source>Include the following information whenever you report a bug:</source>
<translation> Bug </translation>
</message>
<message>
<source>Distribution: %1</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@ -1317,6 +1321,17 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
KeePassX 0.4 </translation>
</message>
</context>
<context>
<name>KeePass2Writer</name>
<message>
<source>Unable to issue challenge-response.</source>
<translation type="unfinished"/>
</message>
<message>
<source>Unable to calculate master key</source>
<translation></translation>
</message>
</context>
<context>
<name>Main</name>
<message>

View File

@ -1,18 +1,18 @@
name: keepassxc
version: 2.2.1
version: 2.2.2
grade: stable
summary: community driven port of the windows application “Keepass Password Safe”
summary: Community-driven port of the Windows application “KeePass Password Safe”
description: |
KeePassXC is an application for people with extremely high demands on secure
personal data management. It has a light interface, is cross platform and
is published under the terms of the GNU General Public License.
personal data management. It has a light interface, is cross-platform and
published under the terms of the GNU General Public License.
confinement: strict
apps:
keepassxc:
command: desktop-launch keepassxc
plugs: [unity7, x11, opengl, gsettings, home, network, network-bind, removable-media, raw-usb]
desktop: share/applications/keepassxc.desktop
desktop: usr/share/applications/org.keepassxc.desktop
cli:
command: keepassxc-cli
plugs: [gsettings, home, removable-media, raw-usb]
@ -23,6 +23,8 @@ parts:
plugin: cmake
configflags:
- -DCMAKE_BUILD_TYPE=Release
- -DCMAKE_INSTALL_PREFIX=/usr
- -DKEEPASSXC_DIST_TYPE=Snap
- -DWITH_TESTS=OFF
- -DWITH_XC_AUTOTYPE=ON
- -DWITH_XC_HTTP=ON
@ -39,6 +41,8 @@ parts:
- libxtst-dev
- libyubikey-dev
- libykpers-1-dev
install: |
sed -i 's|Icon=keepassxc|Icon=${SNAP}/usr/share/icons/hicolor/256x256/apps/keepassxc.png|g' $SNAPCRAFT_PART_INSTALL/usr/share/applications/org.keepassxc.desktop
after: [desktop-qt5]
# Redefine desktop-qt5 stage packages to work with Ubuntu 17.04
@ -55,10 +59,12 @@ parts:
- locales-all
# Overcome limitation in snapd to support URL loading (CTRL+U)
# client needs to install "snapd-xdg-open" on their system
snapd-xdg-open:
source: https://github.com/ubuntu-core/snapd-xdg-open.git
plugin: dump
organize:
data/xdg-open: bin/xdg-open
source-depth: 1
plugin: nil
install: |
install -D -t $SNAPCRAFT_PART_INSTALL/usr/bin/ data/xdg-open
stage-packages:
- dbus

View File

@ -59,6 +59,8 @@ set(keepassx_SOURCES
core/Tools.cpp
core/Translator.cpp
core/Uuid.cpp
core/Base32.h
core/Base32.cpp
cli/PasswordInput.cpp
cli/PasswordInput.h
crypto/Crypto.cpp
@ -138,8 +140,6 @@ set(keepassx_SOURCES
streams/qtiocompressor.cpp
streams/StoreDataStream.cpp
streams/SymmetricCipherStream.cpp
totp/base32.h
totp/base32.cpp
totp/totp.h
totp/totp.cpp
)

View File

@ -16,6 +16,11 @@
#cmakedefine WITH_XC_AUTOTYPE
#cmakedefine WITH_XC_YUBIKEY
#cmakedefine KEEPASSXC_DIST
#cmakedefine KEEPASSXC_DIST_TYPE "@KEEPASSXC_DIST_TYPE@"
#cmakedefine KEEPASSXC_DIST_SNAP
#cmakedefine KEEPASSXC_DIST_APPIMAGE
#cmakedefine HAVE_PR_SET_DUMPABLE 1
#cmakedefine HAVE_RLIMIT_CORE 1
#cmakedefine HAVE_PT_DENY_ATTACH 1

290
src/core/Base32.cpp Normal file
View File

@ -0,0 +1,290 @@
/*
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Conforms to RFC 4648. For details, see: https://tools.ietf.org/html/rfc4648
* Use the functions Base32::addPadding/1, Base32::removePadding/1 or
* Base32::sanitizeInput/1 to fix input or output for a particular
* applications (e.g. to use with Google Authenticator).
*/
#include "Base32.h"
constexpr quint64 MASK_40BIT = quint64(0xF8) << 32;
constexpr quint64 MASK_35BIT = quint64(0x7C0000000);
constexpr quint64 MASK_25BIT = quint64(0x1F00000);
constexpr quint64 MASK_20BIT = quint64(0xF8000);
constexpr quint64 MASK_10BIT = quint64(0x3E0);
constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
constexpr quint8 ALPH_POS_2 = 26;
constexpr quint8 ASCII_2 = static_cast<quint8>('2');
constexpr quint8 ASCII_7 = static_cast<quint8>('7');
constexpr quint8 ASCII_A = static_cast<quint8>('A');
constexpr quint8 ASCII_Z = static_cast<quint8>('Z');
constexpr quint8 ASCII_a = static_cast<quint8>('a');
constexpr quint8 ASCII_z = static_cast<quint8>('z');
constexpr quint8 ASCII_EQ = static_cast<quint8>('=');
QVariant Base32::decode(const QByteArray& encodedData)
{
if (encodedData.size() <= 0) {
return QVariant::fromValue(QByteArray(""));
}
if (encodedData.size() % 8 != 0) {
return QVariant();
}
int nPads = 0;
for (int i = -1; i > -7; --i) {
if ('=' == encodedData[encodedData.size() + i])
++nPads;
}
int specialOffset;
int nSpecialBytes;
switch (nPads) { // in {0, 1, 3, 4, 6}
case 1:
nSpecialBytes = 4;
specialOffset = 3;
break;
case 3:
nSpecialBytes = 3;
specialOffset = 1;
break;
case 4:
nSpecialBytes = 2;
specialOffset = 4;
break;
case 6:
nSpecialBytes = 1;
specialOffset = 2;
break;
default:
nSpecialBytes = 0;
specialOffset = 0;
}
Q_ASSERT(encodedData.size() > 0);
const int nQuanta = encodedData.size() / 8;
const int nBytes = nSpecialBytes > 0 ? (nQuanta - 1) * 5 + nSpecialBytes : nQuanta * 5;
QByteArray data(nBytes, Qt::Uninitialized);
int i = 0;
int o = 0;
while (i < encodedData.size()) {
quint64 quantum = 0;
int nQuantumBytes = 5;
for (int n = 0; n < 8; ++n) {
quint8 ch = static_cast<quint8>(encodedData[i++]);
if ((ASCII_A <= ch && ch <= ASCII_Z) || (ASCII_a <= ch && ch <= ASCII_z)) {
ch -= ASCII_A;
if (ch >= ALPH_POS_2)
ch -= ASCII_a - ASCII_A;
} else {
if (ASCII_2 <= ch && ch <= ASCII_7) {
ch -= ASCII_2;
ch += ALPH_POS_2;
} else {
if (ASCII_EQ == ch) {
if (i == encodedData.size()) {
// finished with special quantum
quantum >>= specialOffset;
nQuantumBytes = nSpecialBytes;
}
continue;
} else {
// illegal character
return QVariant();
}
}
}
quantum <<= 5;
quantum |= ch;
}
const int offset = (nQuantumBytes - 1) * 8;
quint64 mask = quint64(0xFF) << offset;
for (int n = offset; n >= 0 && o < nBytes; n -= 8) {
data[o++] = static_cast<char>((quantum & mask) >> n);
mask >>= 8;
}
}
Q_ASSERT(encodedData.size() == i);
Q_ASSERT(nBytes == o);
return QVariant::fromValue(data);
}
QByteArray Base32::encode(const QByteArray& data)
{
if (data.size() < 1) {
return QByteArray();
}
const int nBits = data.size() * 8;
const int rBits = nBits % 40; // in {0, 8, 16, 24, 32}
const int nQuanta = nBits / 40 + (rBits > 0 ? 1 : 0);
const int nBytes = nQuanta * 8;
QByteArray encodedData(nQuanta * 8, Qt::Uninitialized);
int i = 0;
int o = 0;
int n;
quint64 mask;
quint64 quantum;
// 40-bits of input per input group
while (i + 5 <= data.size()) {
quantum = 0;
for (n = 32; n >= 0; n -= 8) {
quantum |= (static_cast<quint64>(data[i++]) << n);
}
mask = MASK_40BIT;
int index;
for (n = 35; n >= 0; n -= 5) {
index = (quantum & mask) >> n;
encodedData[o++] = alphabet[index];
mask >>= 5;
}
}
// < 40-bits of input at final input group
if (i < data.size()) {
Q_ASSERT(rBits > 0);
quantum = 0;
for (n = rBits - 8; n >= 0; n -= 8)
quantum |= static_cast<quint64>(data[i++]) << n;
switch (rBits) {
case 8: // expand to 10 bits
quantum <<= 2;
mask = MASK_10BIT;
n = 5;
break;
case 16: // expand to 20 bits
quantum <<= 4;
mask = MASK_20BIT;
n = 15;
break;
case 24: // expand to 25 bits
quantum <<= 1;
mask = MASK_25BIT;
n = 20;
break;
default: // expand to 35 bits
Q_ASSERT(rBits == 32);
quantum <<= 3;
mask = MASK_35BIT;
n = 30;
}
while (n >= 0) {
int index = (quantum & mask) >> n;
encodedData[o++] = alphabet[index];
mask >>= 5;
n -= 5;
}
// add pad characters
while (o < encodedData.size()) {
encodedData[o++] = '=';
}
}
Q_ASSERT(data.size() == i);
Q_ASSERT(nBytes == o);
return encodedData;
}
QByteArray Base32::addPadding(const QByteArray& encodedData)
{
if (encodedData.size() <= 0 || encodedData.size() % 8 == 0) {
return encodedData;
}
const int rBytes = encodedData.size() % 8;
// rBytes must be a member of {2, 4, 5, 7}
if (1 == rBytes || 3 == rBytes || 6 == rBytes) {
return encodedData;
}
QByteArray newEncodedData(encodedData);
for (int nPads = 8 - rBytes; nPads > 0; --nPads) {
newEncodedData.append('=');
}
return newEncodedData;
}
QByteArray Base32::removePadding(const QByteArray& encodedData)
{
if (encodedData.size() <= 0 || encodedData.size() % 8 != 0) {
return encodedData; // return same bad input
}
int nPads = 0;
for (int i = -1; i > -7; --i) {
if ('=' == encodedData[encodedData.size() + i]) {
++nPads;
}
}
QByteArray newEncodedData(encodedData);
newEncodedData.remove(encodedData.size() - nPads, nPads);
newEncodedData.resize(encodedData.size() - nPads);
return newEncodedData;
}
QByteArray Base32::sanitizeInput(const QByteArray& encodedData)
{
if (encodedData.size() <= 0) {
return encodedData;
}
QByteArray newEncodedData(encodedData.size(), Qt::Uninitialized);
int i = 0;
for (auto ch : encodedData) {
switch (ch) {
case '0':
newEncodedData[i++] = 'O';
break;
case '1':
newEncodedData[i++] = 'L';
break;
case '8':
newEncodedData[i++] = 'B';
break;
default:
if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ('2' <= ch && ch <= '7')) {
newEncodedData[i++] = ch;
}
}
}
newEncodedData.resize(i);
return addPadding(newEncodedData);
}

42
src/core/Base32.h Normal file
View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Conforms to RFC 4648. For details, see: https://tools.ietf.org/html/rfc4648
* Use the functions Base32::addPadding/1, Base32::removePadding/1 or
* Base32::sanitizeInput/1 to fix input or output for a particular
* applications (e.g. to use with Google Authenticator).
*/
#ifndef BASE32_H
#define BASE32_H
#include <QByteArray>
#include <QVariant>
#include <QtCore/qglobal.h>
class Base32
{
public:
Base32() = default;
Q_REQUIRED_RESULT static QVariant decode(const QByteArray&);
Q_REQUIRED_RESULT static QByteArray encode(const QByteArray&);
Q_REQUIRED_RESULT static QByteArray addPadding(const QByteArray&);
Q_REQUIRED_RESULT static QByteArray removePadding(const QByteArray&);
Q_REQUIRED_RESULT static QByteArray sanitizeInput(const QByteArray&);
};
#endif // BASE32_H

View File

@ -257,6 +257,25 @@ bool Database::hasKey() const
return m_data.hasKey;
}
bool Database::transformKeyWithSeed(const QByteArray& transformSeed)
{
Q_ASSERT(hasKey());
bool ok;
QString errorString;
QByteArray transformedMasterKey =
m_data.key.transform(transformSeed, transformRounds(), &ok, &errorString);
if (!ok) {
return false;
}
m_data.transformSeed = transformSeed;
m_data.transformedMasterKey = transformedMasterKey;
return true;
}
bool Database::verifyKey(const CompositeKey& key) const
{
Q_ASSERT(hasKey());
@ -328,6 +347,15 @@ void Database::emptyRecycleBin()
void Database::merge(const Database* other)
{
m_rootGroup->merge(other->rootGroup());
for (Uuid customIconId : other->metadata()->customIcons().keys()) {
QImage customIcon = other->metadata()->customIcon(customIconId);
if (!this->metadata()->containsCustomIcon(customIconId)) {
qDebug("Adding custom icon %s to database.", qPrintable(customIconId.toHex()));
this->metadata()->addCustomIcon(customIconId, customIcon);
}
}
emit modified();
}

View File

@ -106,6 +106,7 @@ public:
*/
bool setKey(const CompositeKey& key);
bool hasKey() const;
bool transformKeyWithSeed(const QByteArray& transformSeed);
bool verifyKey(const CompositeKey& key) const;
void recycleEntry(Entry* entry);
void recycleGroup(Group* group);

View File

@ -801,28 +801,31 @@ QString Entry::resolvePlaceholder(const QString& str) const
QString Entry::resolveUrl(const QString& url) const
{
#ifdef WITH_XC_HTTP
QString newUrl = url;
if (!url.contains("://")) {
if (!url.isEmpty() && !url.contains("://")) {
// URL doesn't have a protocol, add https by default
newUrl.prepend("https://");
}
QUrl tempUrl = QUrl(newUrl);
if (tempUrl.isValid()) {
if (tempUrl.scheme() == "cmd") {
// URL is a cmd, hopefully the second argument is an URL
QStringList cmd = newUrl.split(" ");
if (cmd.size() > 1) {
return resolveUrl(cmd[1].remove("'").remove("\""));
if (newUrl.startsWith("cmd://")) {
QStringList cmdList = newUrl.split(" ");
for (int i=1; i < cmdList.size(); ++i) {
// Don't pass arguments to the resolveUrl function (they look like URL's)
if (!cmdList[i].startsWith("-") && !cmdList[i].startsWith("/")) {
return resolveUrl(cmdList[i].remove(QRegExp("'|\"")));
}
} else if (tempUrl.scheme() == "http" || tempUrl.scheme() == "https") {
// URL is nice
return tempUrl.url();
}
// No URL in this command
return QString("");
}
#else
Q_UNUSED(url);
#endif
// Validate the URL
QUrl tempUrl = QUrl(newUrl);
if (tempUrl.isValid() && (tempUrl.scheme() == "http" || tempUrl.scheme() == "https")) {
return tempUrl.url();
}
// No valid http URL's found
return QString("");
}

View File

@ -91,17 +91,29 @@ QString FilePath::pluginPath(const QString& name)
QIcon FilePath::applicationIcon()
{
#ifdef KEEPASSXC_DIST_SNAP
return icon("apps", "keepassxc", false);
#else
return icon("apps", "keepassxc");
#endif
}
QIcon FilePath::trayIconLocked()
{
#ifdef KEEPASSXC_DIST_SNAP
return icon("apps", "keepassxc-locked", false);
#else
return icon("apps", "keepassxc-locked");
#endif
}
QIcon FilePath::trayIconUnlocked()
{
#ifdef KEEPASSXC_DIST_SNAP
return icon("apps", "keepassxc-unlocked", false);
#else
return icon("apps", "keepassxc-unlocked");
#endif
}
QIcon FilePath::icon(const QString& category, const QString& name, bool fromTheme)

View File

@ -454,7 +454,7 @@ void Metadata::copyCustomIcons(const QSet<Uuid>& iconList, const Metadata* other
QByteArray Metadata::hashImage(const QImage& image)
{
auto data = QByteArray((char*)image.bits(), image.byteCount());
auto data = QByteArray(reinterpret_cast<const char*>(image.bits()), image.byteCount());
return QCryptographicHash::hash(data, QCryptographicHash::Md5);
}

View File

@ -59,11 +59,11 @@ public:
return m_backend->process(data, ok);
}
inline bool processInPlace(QByteArray& data) Q_REQUIRED_RESULT {
Q_REQUIRED_RESULT inline bool processInPlace(QByteArray& data) {
return m_backend->processInPlace(data);
}
inline bool processInPlace(QByteArray& data, quint64 rounds) Q_REQUIRED_RESULT {
Q_REQUIRED_RESULT inline bool processInPlace(QByteArray& data, quint64 rounds) {
Q_ASSERT(rounds > 0);
return m_backend->processInPlace(data, rounds);
}

View File

@ -29,8 +29,8 @@ public:
virtual bool setIv(const QByteArray& iv) = 0;
virtual QByteArray process(const QByteArray& data, bool* ok) = 0;
virtual bool processInPlace(QByteArray& data) Q_REQUIRED_RESULT = 0;
virtual bool processInPlace(QByteArray& data, quint64 rounds) Q_REQUIRED_RESULT = 0;
Q_REQUIRED_RESULT virtual bool processInPlace(QByteArray& data) = 0;
Q_REQUIRED_RESULT virtual bool processInPlace(QByteArray& data, quint64 rounds) = 0;
virtual bool reset() = 0;
virtual int blockSize() const = 0;

View File

@ -148,9 +148,10 @@ QByteArray SymmetricCipherGcrypt::process(const QByteArray& data, bool* ok)
if (error != 0) {
setErrorString(error);
*ok = false;
} else {
*ok = true;
}
*ok = true;
return result;
}

View File

@ -35,8 +35,8 @@ public:
bool setIv(const QByteArray& iv);
QByteArray process(const QByteArray& data, bool* ok);
bool processInPlace(QByteArray& data) Q_REQUIRED_RESULT;
bool processInPlace(QByteArray& data, quint64 rounds) Q_REQUIRED_RESULT;
Q_REQUIRED_RESULT bool processInPlace(QByteArray& data);
Q_REQUIRED_RESULT bool processInPlace(QByteArray& data, quint64 rounds);
bool reset();
int blockSize() const;

View File

@ -29,7 +29,7 @@ public:
bool init(const QByteArray& key);
QByteArray randomBytes(int size, bool* ok);
QByteArray process(const QByteArray& data, bool* ok);
bool processInPlace(QByteArray& data) Q_REQUIRED_RESULT;
Q_REQUIRED_RESULT bool processInPlace(QByteArray& data);
QString errorString() const;
private:

View File

@ -45,6 +45,7 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db)
m_error = false;
m_errorStr.clear();
QByteArray transformSeed = randomGen()->randomArray(32);
QByteArray masterSeed = randomGen()->randomArray(32);
QByteArray encryptionIV = randomGen()->randomArray(16);
QByteArray protectedStreamKey = randomGen()->randomArray(32);
@ -52,7 +53,12 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db)
QByteArray endOfHeader = "\r\n\r\n";
if (db->challengeMasterSeed(masterSeed) == false) {
raiseError("Unable to issue challenge-response.");
raiseError(tr("Unable to issue challenge-response."));
return;
}
if (!db->transformKeyWithSeed(transformSeed)) {
raiseError(tr("Unable to calculate master key"));
return;
}

View File

@ -18,6 +18,8 @@
#ifndef KEEPASSX_KEEPASS2WRITER_H
#define KEEPASSX_KEEPASS2WRITER_H
#include <QCoreApplication>
#include "format/KeePass2.h"
#include "keys/CompositeKey.h"
@ -26,6 +28,8 @@ class QIODevice;
class KeePass2Writer
{
Q_DECLARE_TR_FUNCTIONS(KeePass2Writer)
public:
KeePass2Writer();
void writeDatabase(QIODevice* device, Database* db);

View File

@ -55,10 +55,14 @@ AboutDialog::AboutDialog(QWidget* parent)
QString debugInfo = "KeePassXC - ";
debugInfo.append(tr("Version %1\n").arg(KEEPASSX_VERSION));
if (!commitHash.isEmpty()) {
debugInfo.append(tr("Revision: %1").arg(commitHash).append("\n\n"));
debugInfo.append(tr("Revision: %1").arg(commitHash.left(7)).append("\n"));
}
debugInfo.append(QString("%1\n- Qt %2\n- %3\n\n")
#ifdef KEEPASSXC_DIST
debugInfo.append(tr("Distribution: %1").arg(KEEPASSXC_DIST_TYPE).append("\n"));
#endif
debugInfo.append("\n").append(QString("%1\n- Qt %2\n- %3\n\n")
.arg(tr("Libraries:"))
.arg(QString::fromLocal8Bit(qVersion()))
.arg(Crypto::backendVersion()));

View File

@ -70,9 +70,6 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
connect(m_ui->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey()));
connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(activateChallengeResponse()));
connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection);
#else
m_ui->checkChallengeResponse->setVisible(false);
m_ui->buttonRedetectYubikey->setVisible(false);
@ -98,10 +95,24 @@ void DatabaseOpenWidget::showEvent(QShowEvent* event)
m_ui->editPassword->setFocus();
#ifdef WITH_XC_YUBIKEY
connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(detectComplete()), SLOT(yubikeyDetectComplete()), Qt::QueuedConnection);
connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection);
pollYubikey();
#endif
}
void DatabaseOpenWidget::hideEvent(QHideEvent* event)
{
DialogyWidget::hideEvent(event);
#ifdef WITH_XC_YUBIKEY
// Don't listen to any Yubikey events if we are hidden
disconnect(YubiKey::instance(), 0, this, 0);
#endif
}
void DatabaseOpenWidget::load(const QString& filename)
{
m_filename = filename;
@ -151,7 +162,10 @@ void DatabaseOpenWidget::enterKey(const QString& pw, const QString& keyFile)
void DatabaseOpenWidget::openDatabase()
{
KeePass2Reader reader;
CompositeKey masterKey = databaseKey();
QSharedPointer<CompositeKey> masterKey = databaseKey();
if (masterKey.isNull()) {
return;
}
QFile file(m_filename);
if (!file.open(QIODevice::ReadOnly)) {
@ -163,7 +177,7 @@ void DatabaseOpenWidget::openDatabase()
delete m_db;
}
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
m_db = reader.readDatabase(&file, masterKey);
m_db = reader.readDatabase(&file, *masterKey);
QApplication::restoreOverrideCursor();
if (m_db) {
@ -171,20 +185,19 @@ void DatabaseOpenWidget::openDatabase()
m_ui->messageWidget->animatedHide();
}
emit editFinished(true);
}
else {
m_ui->messageWidget->showMessage(tr("Unable to open the database.")
.append("\n").append(reader.errorString()), MessageWidget::Error);
} else {
m_ui->messageWidget->showMessage(tr("Unable to open the database.").append("\n").append(reader.errorString()),
MessageWidget::Error);
m_ui->editPassword->clear();
}
}
CompositeKey DatabaseOpenWidget::databaseKey()
QSharedPointer<CompositeKey> DatabaseOpenWidget::databaseKey()
{
CompositeKey masterKey;
auto masterKey = QSharedPointer<CompositeKey>::create();
if (m_ui->checkPassword->isChecked()) {
masterKey.addKey(PasswordKey(m_ui->editPassword->text()));
masterKey->addKey(PasswordKey(m_ui->editPassword->text()));
}
QHash<QString, QVariant> lastKeyFiles = config()->get("LastKeyFiles").toHash();
@ -195,11 +208,11 @@ CompositeKey DatabaseOpenWidget::databaseKey()
QString keyFilename = m_ui->comboKeyFile->currentText();
QString errorMsg;
if (!key.load(keyFilename, &errorMsg)) {
m_ui->messageWidget->showMessage(tr("Can't open key file").append(":\n")
.append(errorMsg), MessageWidget::Error);
return CompositeKey();
m_ui->messageWidget->showMessage(tr("Can't open key file").append(":\n").append(errorMsg),
MessageWidget::Error);
return QSharedPointer<CompositeKey>();
}
masterKey.addKey(key);
masterKey->addKey(key);
lastKeyFiles[m_filename] = keyFilename;
} else {
lastKeyFiles.remove(m_filename);
@ -226,9 +239,9 @@ CompositeKey DatabaseOpenWidget::databaseKey()
// read blocking mode from LSB and slot index number from second LSB
bool blocking = comboPayload & 1;
int slot = comboPayload >> 1;
auto key = QSharedPointer<YkChallengeResponseKey>(new YkChallengeResponseKey(slot, blocking));
masterKey.addChallengeResponseKey(key);
int slot = comboPayload >> 1;
auto key = QSharedPointer<YkChallengeResponseKey>(new YkChallengeResponseKey(slot, blocking));
masterKey->addChallengeResponseKey(key);
}
#endif
@ -283,10 +296,6 @@ void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking)
YkChallengeResponseKey yk(slot, blocking);
// add detected YubiKey to combo box and encode blocking mode in LSB, slot number in second LSB
m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1) | blocking));
m_ui->comboChallengeResponse->setEnabled(true);
m_ui->checkChallengeResponse->setEnabled(true);
m_ui->buttonRedetectYubikey->setEnabled(true);
m_ui->yubikeyProgress->setVisible(false);
if (config()->get("RememberLastKeyFiles").toBool()) {
QHash<QString, QVariant> lastChallengeResponse = config()->get("LastChallengeResponse").toHash();
@ -296,6 +305,14 @@ void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking)
}
}
void DatabaseOpenWidget::yubikeyDetectComplete()
{
m_ui->comboChallengeResponse->setEnabled(true);
m_ui->checkChallengeResponse->setEnabled(true);
m_ui->buttonRedetectYubikey->setEnabled(true);
m_ui->yubikeyProgress->setVisible(false);
}
void DatabaseOpenWidget::noYubikeyFound()
{
m_ui->buttonRedetectYubikey->setEnabled(true);

View File

@ -51,7 +51,8 @@ signals:
protected:
void showEvent(QShowEvent* event) override;
CompositeKey databaseKey();
void hideEvent(QHideEvent* event) override;
QSharedPointer<CompositeKey> databaseKey();
protected slots:
virtual void openDatabase();
@ -63,6 +64,7 @@ private slots:
void activateChallengeResponse();
void browseKeyFile();
void yubikeyDetected(int slot, bool blocking);
void yubikeyDetectComplete();
void noYubikeyFound();
protected:

View File

@ -129,7 +129,12 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw,
while (i.hasNext()) {
i.next();
if (i.value().canonicalFilePath == canonicalFilePath) {
setCurrentIndex(databaseIndex(i.key()));
if (!i.value().dbWidget->dbHasKey() && !(pw.isNull() && keyFile.isEmpty())) {
// If the database is locked and a pw or keyfile is provided, unlock it
i.value().dbWidget->switchToOpenDatabase(i.value().filePath, pw, keyFile);
} else {
setCurrentIndex(databaseIndex(i.key()));
}
return;
}
}
@ -181,6 +186,7 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw,
lockFile->tryLock();
}
} else {
delete lockFile;
return;
}
}
@ -203,7 +209,7 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw,
updateLastDatabases(dbStruct.filePath);
if (!pw.isNull() || !keyFile.isEmpty()) {
if (!(pw.isNull() && keyFile.isEmpty())) {
dbStruct.dbWidget->switchToOpenDatabase(dbStruct.filePath, pw, keyFile);
}
else {

View File

@ -302,6 +302,7 @@ void EditWidgetIcons::addCustomIcon(const QImage &icon)
}
// Select the new or existing icon
updateRadioButtonCustomIcons();
QModelIndex index = m_customIconModel->indexFromUuid(uuid);
m_ui->customIconsView->setCurrentIndex(index);
}

View File

@ -156,7 +156,7 @@ void SettingsWidget::loadSettings()
m_secUi->lockDatabaseIdleSpinBox->setValue(config()->get("security/lockdatabaseidlesec").toInt());
m_secUi->lockDatabaseMinimizeCheckBox->setChecked(config()->get("security/lockdatabaseminimize").toBool());
m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked(config()->get("security/lockdatabasescreenlock").toBool());
m_secUi->lockDatabaseOnScreenLockCheckBox->setChecked(config()->get("security/IconDownloadFallbackToGoogle").toBool());
m_secUi->fallbackToGoogle->setChecked(config()->get("security/IconDownloadFallbackToGoogle").toBool());
m_secUi->passwordCleartextCheckBox->setChecked(config()->get("security/passwordscleartext").toBool());
m_secUi->passwordRepeatCheckBox->setChecked(config()->get("security/passwordsrepeat").toBool());

View File

@ -25,6 +25,7 @@
#include <ykdef.h>
#include <ykstatus.h>
#include "core/Tools.h"
#include "core/Global.h"
#include "crypto/Random.h"
@ -112,23 +113,36 @@ bool YubiKey::deinit()
void YubiKey::detect()
{
if (init()) {
for (int i = 1; i < 3; i++) {
YubiKey::ChallengeResult result;
QByteArray rand = randomGen()->randomArray(1);
QByteArray resp;
bool found = false;
if (init()) {
YubiKey::ChallengeResult result;
QByteArray rand = randomGen()->randomArray(1);
QByteArray resp;
// Check slot 1 and 2 for Challenge-Response HMAC capability
for (int i = 1; i <= 2; ++i) {
result = challenge(i, false, rand, resp);
if (result == YubiKey::ALREADY_RUNNING) {
emit alreadyRunning();
return;
} else if (result != YubiKey::ERROR) {
emit detected(i, result == YubiKey::WOULDBLOCK);
return;
if (result == ALREADY_RUNNING) {
// Try this slot again after waiting
Tools::sleep(300);
result = challenge(i, false, rand, resp);
}
if (result != ALREADY_RUNNING && result != ERROR) {
emit detected(i, result == WOULDBLOCK);
found = true;
}
// Wait between slots to let the yubikey settle
Tools::sleep(150);
}
}
emit notFound();
if (!found) {
emit notFound();
} else {
emit detectComplete();
}
}
bool YubiKey::getSerial(unsigned int& serial)
@ -160,6 +174,7 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte
}
// yk_challenge_response() insists on 64 byte response buffer */
response.clear();
response.resize(64);
/* The challenge sent to the yubikey should always be 64 bytes for

View File

@ -87,6 +87,11 @@ signals:
*/
void detected(int slot, bool blocking);
/**
* Emitted when detection is complete
*/
void detectComplete();
/**
* Emitted when the YubiKey was challenged and has returned a response.
*/

View File

@ -1,68 +0,0 @@
// Base32 implementation
// Source: https://github.com/google/google-authenticator-libpam/blob/master/src/base32.c
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
// Modifications Copyright 2017 KeePassXC team <team@keepassxc.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "base32.h"
Base32::Base32()
{
}
QByteArray Base32::base32_decode(const QByteArray encoded)
{
QByteArray result;
int buffer = 0;
int bitsLeft = 0;
for (char ch : encoded) {
if (ch == 0 || ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-' || ch == '=') {
continue;
}
buffer <<= 5;
// Deal with commonly mistyped characters
if (ch == '0') {
ch = 'O';
} else if (ch == '1') {
ch = 'L';
} else if (ch == '8') {
ch = 'B';
}
// Look up one base32 digit
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
ch = (ch & 0x1F) - 1;
} else if (ch >= '2' && ch <= '7') {
ch -= '2' - 26;
} else {
return QByteArray();
}
buffer |= ch;
bitsLeft += 5;
if (bitsLeft >= 8) {
result.append(static_cast<char> (buffer >> (bitsLeft - 8)));
bitsLeft -= 8;
}
}
return result;
}

View File

@ -1,34 +0,0 @@
// Base32 implementation
// Source: https://github.com/google/google-authenticator-libpam/blob/master/src/base32.h
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
// Modifications Copyright 2017 KeePassXC team <team@keepassxc.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BASE32_H
#define BASE32_H
#include <QtCore/qglobal.h>
#include <QByteArray>
class Base32
{
public:
Base32();
static QByteArray base32_decode(const QByteArray encoded);
};
#endif //BASE32_H

View File

@ -17,16 +17,16 @@
*/
#include "totp.h"
#include "base32.h"
#include <cmath>
#include <QtEndian>
#include <QRegExp>
#include <QDateTime>
#include "core/Base32.h"
#include <QCryptographicHash>
#include <QDateTime>
#include <QMessageAuthenticationCode>
#include <QRegExp>
#include <QUrl>
#include <QUrlQuery>
#include <QVariant>
#include <QtEndian>
#include <cmath>
const quint8 QTotp::defaultStep = 30;
const quint8 QTotp::defaultDigits = 6;
@ -35,7 +35,7 @@ QTotp::QTotp()
{
}
QString QTotp::parseOtpString(QString key, quint8 &digits, quint8 &step)
QString QTotp::parseOtpString(QString key, quint8& digits, quint8& step)
{
QUrl url(key);
@ -58,7 +58,6 @@ QString QTotp::parseOtpString(QString key, quint8 &digits, quint8 &step)
step = q_step;
}
} else {
// Compatibility with "KeeOtp" plugin string format
QRegExp rx("key=(.+)", Qt::CaseInsensitive, QRegExp::RegExp);
@ -93,30 +92,59 @@ QString QTotp::parseOtpString(QString key, quint8 &digits, quint8 &step)
return seed;
}
QString QTotp::generateTotp(const QByteArray key, quint64 time,
const quint8 numDigits = defaultDigits, const quint8 step = defaultStep)
QString QTotp::generateTotp(const QByteArray key,
quint64 time,
const quint8 numDigits = defaultDigits,
const quint8 step = defaultStep)
{
quint64 current = qToBigEndian(time / step);
QByteArray secret = Base32::base32_decode(key);
if (secret.isEmpty()) {
QVariant secret = Base32::decode(Base32::sanitizeInput(key));
if (secret.isNull()) {
return "Invalid TOTP secret key";
}
QMessageAuthenticationCode code(QCryptographicHash::Sha1);
code.setKey(secret);
code.setKey(secret.toByteArray());
code.addData(QByteArray(reinterpret_cast<char*>(&current), sizeof(current)));
QByteArray hmac = code.result();
int offset = (hmac[hmac.length() - 1] & 0xf);
// clang-format off
int binary =
((hmac[offset] & 0x7f) << 24)
| ((hmac[offset + 1] & 0xff) << 16)
| ((hmac[offset + 2] & 0xff) << 8)
| (hmac[offset + 3] & 0xff);
// clang-format on
quint32 digitsPower = pow(10, numDigits);
quint64 password = binary % digitsPower;
return QString("%1").arg(password, numDigits, 10, QChar('0'));
}
// See: https://github.com/google/google-authenticator/wiki/Key-Uri-Format
QUrl QTotp::generateOtpString(const QString& secret,
const QString& type,
const QString& issuer,
const QString& username,
const QString& algorithm,
const quint8& digits,
const quint8& step)
{
QUrl keyUri;
keyUri.setScheme("otpauth");
keyUri.setHost(type);
keyUri.setPath(QString("/%1:%2").arg(issuer).arg(username));
QUrlQuery parameters;
parameters.addQueryItem("secret", secret);
parameters.addQueryItem("issuer", issuer);
parameters.addQueryItem("algorithm", algorithm);
parameters.addQueryItem("digits", QString::number(digits));
parameters.addQueryItem("period", QString::number(step));
keyUri.setQuery(parameters);
return keyUri;
}

View File

@ -21,12 +21,21 @@
#include <QtCore/qglobal.h>
class QUrl;
class QTotp
{
public:
QTotp();
static QString parseOtpString(QString rawSecret, quint8 &digits, quint8 &step);
static QString parseOtpString(QString rawSecret, quint8& digits, quint8& step);
static QString generateTotp(const QByteArray key, quint64 time, const quint8 numDigits, const quint8 step);
static QUrl generateOtpString(const QString& secret,
const QString& type,
const QString& issuer,
const QString& username,
const QString& algorithm,
const quint8& digits,
const quint8& step);
static const quint8 defaultStep;
static const quint8 defaultDigits;
};

View File

@ -161,6 +161,9 @@ add_unit_test(NAME testentry SOURCES TestEntry.cpp
add_unit_test(NAME testtotp SOURCES TestTotp.cpp
LIBS ${TEST_LIBRARIES})
add_unit_test(NAME testbase32 SOURCES TestBase32.cpp
LIBS ${TEST_LIBRARIES})
add_unit_test(NAME testcsvparser SOURCES TestCsvParser.cpp
LIBS ${TEST_LIBRARIES})

330
tests/TestBase32.cpp Normal file
View File

@ -0,0 +1,330 @@
/*
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TestBase32.h"
#include "core/Base32.h"
#include <QTest>
QTEST_GUILESS_MAIN(TestBase32)
void TestBase32::testDecode()
{
// 3 quanta, all upper case + padding
QByteArray encodedData = "JBSWY3DPEB3W64TMMQXC4LQ=";
QVariant data = Base32::decode(encodedData);
QString expectedData = "Hello world...";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 4 quanta, all upper case
encodedData = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ";
data = Base32::decode(encodedData);
expectedData = "12345678901234567890";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 4 quanta, all lower case
encodedData = "gezdgnbvgy3tqojqgezdgnbvgy3tqojq";
data = Base32::decode(encodedData);
expectedData = "12345678901234567890";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 4 quanta, mixed upper and lower case
encodedData = "Gezdgnbvgy3tQojqgezdGnbvgy3tQojQ";
data = Base32::decode(encodedData);
expectedData = "12345678901234567890";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 1 pad characters
encodedData = "ORSXG5A=";
data = Base32::decode(encodedData);
expectedData = "test";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 3 pad characters
encodedData = "L5PV6===";
data = Base32::decode(encodedData);
expectedData = "___";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// 4 pad characters
encodedData = "MZXW6IDCMFZA====";
data = Base32::decode(encodedData);
expectedData = "foo bar";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// six pad characters
encodedData = "MZXW6YTBOI======";
data = Base32::decode(encodedData);
expectedData = "foobar";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "IA======";
data = Base32::decode(encodedData);
expectedData = "@";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
// error: illegal character
encodedData = "1MZXW6YTBOI=====";
data = Base32::decode(encodedData);
QVERIFY(data.isNull());
// error: missing pad character
encodedData = "MZXW6YTBOI=====";
data = Base32::decode(encodedData);
QVERIFY(data.isNull());
// RFC 4648 test vectors
encodedData = "";
data = Base32::decode(encodedData);
expectedData = "";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MY======";
data = Base32::decode(encodedData);
expectedData = "f";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MZXQ====";
data = Base32::decode(encodedData);
expectedData = "fo";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MZXW6===";
data = Base32::decode(encodedData);
QVERIFY(!data.isNull());
expectedData = "foo";
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MZXW6YQ=";
data = Base32::decode(encodedData);
expectedData = "foob";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MZXW6YTB";
expectedData = "fooba";
data = Base32::decode(encodedData);
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
encodedData = "MZXW6YTBOI======";
data = Base32::decode(encodedData);
expectedData = "foobar";
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), expectedData);
QVERIFY(data.value<QByteArray>().size() == expectedData.size());
}
void TestBase32::testEncode()
{
QByteArray data = "Hello world...";
QByteArray encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("JBSWY3DPEB3W64TMMQXC4LQ="));
data = "12345678901234567890";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"));
data = "012345678901234567890";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("GAYTEMZUGU3DOOBZGAYTEMZUGU3DOOBZGA======"));
data = "test";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("ORSXG5A="));
data = "___";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("L5PV6==="));
data = "foo bar";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXW6IDCMFZA===="));
data = "@";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("IA======"));
// RFC 4648 test vectors
data = "";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray(""));
data = "f";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MY======"));
data = "fo";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXQ===="));
data = "foo";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXW6==="));
data = "foob";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXW6YQ="));
data = "fooba";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXW6YTB"));
data = "foobar";
encodedData = Base32::encode(data);
QCOMPARE(encodedData, QByteArray("MZXW6YTBOI======"));
}
void TestBase32::testAddPadding()
{
// Empty. Invalid, returns input.
QByteArray data = "";
QByteArray paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, data);
// One byte of encoded data. Invalid, returns input.
data = "B";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, data);
// Two bytes of encoded data.
data = "BB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, QByteArray("BB======"));
// Three bytes of encoded data. Invalid, returns input.
data = "BBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, data);
// Four bytes of encoded data.
data = "BBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, QByteArray("BBBB===="));
// Five bytes of encoded data.
data = "BBBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, QByteArray("BBBBB==="));
// Six bytes of encoded data. Invalid, returns input.
data = "BBBBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, data);
// Seven bytes of encoded data.
data = "BBBBBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, QByteArray("BBBBBBB="));
// Eight bytes of encoded data. Valid, but returns same as input.
data = "BBBBBBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, data);
// More than eight bytes (8+5).
data = "AAAAAAAABBBBB";
paddedData = Base32::addPadding(data);
QCOMPARE(paddedData, QByteArray("AAAAAAAABBBBB==="));
}
void TestBase32::testRemovePadding()
{
QByteArray data = "";
QByteArray unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, data);
data = "AAAAAAAABB======";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, QByteArray("AAAAAAAABB"));
data = "BBBB====";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, QByteArray("BBBB"));
data = "AAAAAAAABBBBB===";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, QByteArray("AAAAAAAABBBBB"));
data = "BBBBBBB=";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, QByteArray("BBBBBBB"));
// Invalid: 7 bytes of data. Returns same as input.
data = "IIIIIII";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, data);
// Invalid: more padding than necessary. Returns same as input.
data = "AAAAAAAABBBB=====";
unpaddedData = Base32::removePadding(data);
QCOMPARE(unpaddedData, data);
}
void TestBase32::testSanitizeInput()
{
// sanitize input (white space + missing padding)
QByteArray encodedData = "JBSW Y3DP EB3W 64TM MQXC 4LQA";
auto data = Base32::decode(Base32::sanitizeInput(encodedData));
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), QString("Hello world..."));
// sanitize input (typo + missing padding)
encodedData = "J8SWY3DPE83W64TMMQXC4LQA";
data = Base32::decode(Base32::sanitizeInput(encodedData));
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), QString("Hello world..."));
// sanitize input (other illegal characters)
encodedData = "J8SWY3D[PE83W64TMMQ]XC!4LQA";
data = Base32::decode(Base32::sanitizeInput(encodedData));
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), QString("Hello world..."));
// sanitize input (NUL character)
encodedData = "J8SWY3DPE83W64TMMQXC4LQA";
encodedData.insert(3, '\0');
data = Base32::decode(Base32::sanitizeInput(encodedData));
QVERIFY(!data.isNull());
QCOMPARE(data.toString(), QString("Hello world..."));
}

37
tests/TestBase32.h Normal file
View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KEEPASSX_TESTBASE32_H
#define KEEPASSX_TESTBASE32_H
#include <QObject>
class Base32;
class TestBase32 : public QObject
{
Q_OBJECT
private slots:
void testEncode();
void testDecode();
void testAddPadding();
void testRemovePadding();
void testSanitizeInput();
};
#endif // KEEPASSX_TESTBASE32_H

View File

@ -16,6 +16,7 @@
*/
#include "TestEntry.h"
#include "config-keepassx-tests.h"
#include <QTest>
@ -130,3 +131,30 @@ void TestEntry::testClone()
delete entryOrg;
}
void TestEntry::testResolveUrl()
{
Entry* entry = new Entry();
QString testUrl("www.google.com");
QString testCmd("cmd://firefox " + testUrl);
QString testComplexCmd("cmd://firefox --start-now --url 'http://" + testUrl + "' --quit");
QString nonHttpUrl("ftp://google.com");
QString noUrl("random text inserted here");
// Test standard URL's
QCOMPARE(entry->resolveUrl(""), QString(""));
QCOMPARE(entry->resolveUrl(testUrl), "https://" + testUrl);
QCOMPARE(entry->resolveUrl("http://" + testUrl), "http://" + testUrl);
// Test cmd:// with no URL
QCOMPARE(entry->resolveUrl("cmd://firefox"), QString(""));
QCOMPARE(entry->resolveUrl("cmd://firefox --no-url"), QString(""));
// Test cmd:// with URL's
QCOMPARE(entry->resolveUrl(testCmd), "https://" + testUrl);
QCOMPARE(entry->resolveUrl(testComplexCmd), "http://" + testUrl);
// Test non-http URL
QCOMPARE(entry->resolveUrl(nonHttpUrl), QString(""));
// Test no URL
QCOMPARE(entry->resolveUrl(noUrl), QString(""));
delete entry;
}

View File

@ -31,6 +31,7 @@ private slots:
void testHistoryItemDeletion();
void testCopyDataFrom();
void testClone();
void testResolveUrl();
};
#endif // KEEPASSX_TESTENTRY_H

View File

@ -18,15 +18,14 @@
#include "TestTotp.h"
#include <QTest>
#include <QTime>
#include <QDateTime>
#include <QtEndian>
#include <QTest>
#include <QTextCodec>
#include <QTime>
#include <QtEndian>
#include "crypto/Crypto.h"
#include "totp/totp.h"
#include "totp/base32.h"
QTEST_GUILESS_MAIN(TestTotp)
@ -35,12 +34,13 @@ void TestTotp::initTestCase()
QVERIFY(Crypto::init());
}
void TestTotp::testParseSecret()
{
quint8 digits = 0;
quint8 step = 0;
QString secret = "otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30";
QString secret = "otpauth://totp/"
"ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm="
"SHA1&digits=6&period=30";
QCOMPARE(QTotp::parseOtpString(secret, digits, step), QString("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ"));
QCOMPARE(digits, quint8(6));
QCOMPARE(step, quint8(30));
@ -60,25 +60,6 @@ void TestTotp::testParseSecret()
QCOMPARE(step, quint8(30));
}
void TestTotp::testBase32()
{
QByteArray key = QString("JBSW Y3DP EB3W 64TM MQXC 4LQA").toLatin1();
QByteArray secret = Base32::base32_decode(key);
QCOMPARE(QString::fromLatin1(secret), QString("Hello world..."));
key = QString("gezdgnbvgy3tqojqgezdgnbvgy3tqojq").toLatin1();
secret = Base32::base32_decode(key);
QCOMPARE(QString::fromLatin1(secret), QString("12345678901234567890"));
key = QString("ORSXG5A=").toLatin1();
secret = Base32::base32_decode(key);
QCOMPARE(QString::fromLatin1(secret), QString("test"));
key = QString("MZXW6YTBOI======").toLatin1();
secret = Base32::base32_decode(key);
QCOMPARE(QString::fromLatin1(secret), QString("foobar"));
}
void TestTotp::testTotpCode()
{
// Test vectors from RFC 6238

View File

@ -30,7 +30,6 @@ class TestTotp : public QObject
private slots:
void initTestCase();
void testParseSecret();
void testBase32();
void testTotpCode();
};