summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 19:22:06 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 19:22:06 +0000
commit5ecaa3a7f798e0da8bd46ed1757c21c0d6334505 (patch)
tree6947e7c36a88bbb00e0a950ee6b33e2aaf6254ff
parentAdding upstream version 0.17.0. (diff)
downloadrnp-upstream.tar.xz
rnp-upstream.zip
Adding upstream version 0.17.1.upstream/0.17.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.cirrus.yml8
-rw-r--r--.github/workflows/centos-and-fedora.yml317
-rw-r--r--.github/workflows/coverity.yml63
-rw-r--r--.github/workflows/debian.yml92
-rw-r--r--.github/workflows/fuzzing.yml2
-rw-r--r--.github/workflows/macos.yml26
-rw-r--r--.github/workflows/nix.yml2
-rw-r--r--.github/workflows/opensuse.yml96
-rw-r--r--.github/workflows/ubuntu.yml78
-rw-r--r--.github/workflows/windows-native.yml46
-rw-r--r--.gitignore3
-rw-r--r--.gitmodules6
-rw-r--r--CHANGELOG.md13
-rw-r--r--CMakeLists.txt50
-rw-r--r--README.adoc1
-rwxr-xr-xci-legacy/build_package_rpm.sh (renamed from ci/build_package_rpm.sh)0
-rw-r--r--ci-legacy/env-common.inc.sh (renamed from ci/env-common.inc.sh)4
-rw-r--r--ci-legacy/env-freebsd.inc.sh (renamed from ci/env-freebsd.inc.sh)0
-rw-r--r--ci-legacy/env-linux.inc.sh (renamed from ci/env-linux.inc.sh)0
-rw-r--r--ci-legacy/env.inc.sh (renamed from ci/env.inc.sh)0
-rw-r--r--ci-legacy/gha/setup-env.inc.sh (renamed from ci/gha/setup-env.inc.sh)0
-rwxr-xr-xci-legacy/install.sh (renamed from ci/install.sh)0
-rwxr-xr-xci-legacy/install_cacheable_dependencies.sh (renamed from ci/install_cacheable_dependencies.sh)0
-rwxr-xr-xci-legacy/install_noncacheable_dependencies.sh (renamed from ci/install_noncacheable_dependencies.sh)0
-rw-r--r--ci-legacy/lib/cacheable_install_functions.inc.sh (renamed from ci/lib/cacheable_install_functions.inc.sh)24
-rw-r--r--ci-legacy/lib/install_functions.inc.sh (renamed from ci/lib/install_functions.inc.sh)0
-rwxr-xr-xci-legacy/local.sh (renamed from ci/local.sh)0
-rwxr-xr-xci-legacy/main.sh (renamed from ci/main.sh)0
-rwxr-xr-xci-legacy/run.sh (renamed from ci/run.sh)0
-rwxr-xr-xci-legacy/style-check.sh (renamed from ci/style-check.sh)0
-rwxr-xr-xci-legacy/success.sh (renamed from ci/success.sh)0
-rw-r--r--ci-legacy/utils.inc.sh (renamed from ci/utils.inc.sh)0
-rw-r--r--ci/botan3-modules46
-rw-r--r--ci/docker/Makefile11
-rw-r--r--ci/docker/fedora35.Dockerfile52
-rwxr-xr-xci/docker/fedora35.test-locally22
-rwxr-xr-xci/tests/ci-tests.sh6
-rwxr-xr-xci/tests/deb-tests.sh2
-rwxr-xr-xci/tests/pk-tests.sh31
-rwxr-xr-xci/tests/pkg-tests.sh2
-rwxr-xr-xci/tests/rpm-tests.sh2
-rw-r--r--cmake/CTestCostData.txt431
-rw-r--r--cmake/Modules/FindBotan.cmake168
-rw-r--r--cmake/Modules/FindBotan2.cmake131
-rw-r--r--cmake/Modules/FindOpenSSLFeatures.cmake5
-rw-r--r--cmake/Modules/findopensslfeatures.c51
-rw-r--r--cmake/packaging.cmake1
-rw-r--r--codecov.yml27
-rw-r--r--docs/develop.adoc4
-rw-r--r--docs/installation.adoc29
-rwxr-xr-xgit-hooks/pre-commit.sh2
-rw-r--r--include/rnp/rnp.h16
-rw-r--r--src/common/uniwin.h4
-rw-r--r--src/fuzzing/keyring.c13
-rw-r--r--src/fuzzing/keyring_kbx.c12
-rw-r--r--src/fuzzing/sigimport.c11
-rw-r--r--src/fuzzing/verify.c13
-rw-r--r--src/fuzzing/verify_detached.c19
-rwxr-xr-xsrc/lib/CMakeLists.txt105
-rw-r--r--src/lib/config.h.in4
-rw-r--r--src/lib/crypto.cpp4
-rw-r--r--src/lib/crypto/backend_version.cpp12
-rw-r--r--src/lib/crypto/bn.h78
-rw-r--r--src/lib/crypto/cipher.hpp5
-rw-r--r--src/lib/crypto/cipher_botan.cpp8
-rw-r--r--src/lib/crypto/dl_ossl.cpp118
-rw-r--r--src/lib/crypto/dsa_ossl.cpp174
-rw-r--r--src/lib/crypto/ec_ossl.cpp181
-rw-r--r--src/lib/crypto/ecdh.cpp13
-rw-r--r--src/lib/crypto/ecdh_ossl.cpp56
-rw-r--r--src/lib/crypto/elgamal_ossl.cpp99
-rw-r--r--src/lib/crypto/rsa.cpp8
-rw-r--r--src/lib/crypto/rsa_ossl.cpp351
-rw-r--r--src/lib/crypto/symmetric.cpp12
-rw-r--r--src/lib/crypto/symmetric_ossl.cpp52
-rw-r--r--src/lib/rnp.cpp23
-rw-r--r--src/librekey/g23_sexp.hpp4
-rw-r--r--src/librepgp/stream-armor.cpp6
-rw-r--r--src/librepgp/stream-parse.cpp6
-rw-r--r--src/librepgp/stream-sig.cpp11
-rw-r--r--src/librepgp/stream-sig.h2
-rw-r--r--src/rnp/fficli.cpp2
-rw-r--r--src/rnp/rnp.cpp8
-rw-r--r--src/rnpkeys/main.cpp6
-rw-r--r--src/rnpkeys/rnpkeys.cpp6
-rw-r--r--src/tests/CMakeLists.txt29
-rw-r--r--src/tests/cipher.cpp96
-rw-r--r--src/tests/cipher_cxx.cpp9
-rw-r--r--src/tests/cli_common.py4
-rwxr-xr-xsrc/tests/cli_tests.py72
-rw-r--r--src/tests/data/test_messages/message.aead-windows-issuebin0 -> 156183 bytes
-rw-r--r--src/tests/data/test_messages/message.aead-windows-issue-botanbin0 -> 156111 bytes
-rw-r--r--src/tests/data/test_messages/message.aead-windows-issue2bin0 -> 193572 bytes
-rw-r--r--src/tests/data/test_messages/message.txt.signed-mimemodebin0 -> 260 bytes
-rw-r--r--src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line-2.asc11
-rw-r--r--src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line.asc11
-rw-r--r--src/tests/ffi-enc.cpp37
-rw-r--r--src/tests/ffi-key-sig.cpp5
-rw-r--r--src/tests/ffi-key.cpp27
-rw-r--r--src/tests/ffi.cpp32
-rw-r--r--src/tests/generatekey.cpp10
-rw-r--r--src/tests/key-add-userid.cpp2
-rw-r--r--src/tests/load-pgp.cpp13
-rw-r--r--src/tests/rnp_tests.cpp5
-rw-r--r--src/tests/rnp_tests.h3
-rw-r--r--src/tests/streams.cpp67
-rw-r--r--src/tests/support.cpp8
-rw-r--r--src/tests/support.h2
-rw-r--r--version.txt2
109 files changed, 2502 insertions, 1239 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 0d0115f..2fd7d95 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -23,7 +23,7 @@
# POSSIBILITY OF SUCH DAMAGE.
freebsd_instance:
- image: freebsd-12-3-release-amd64
+ image: freebsd-13-2-release-amd64
task:
name: build
@@ -31,9 +31,9 @@ task:
skip: "!changesInclude('.cirrus.yml') && changesIncludeOnly('/*.sh', '/.*', '/_*', 'Brewfile', 'docs/**', '**.adoc', '**.md', '**.nix', 'flake.lock', '.github/**') || $CIRRUS_CHANGE_MESSAGE =~ '.*skip ci.*'"
env:
matrix:
- - { CIRRUS_CLONE_SUBMODULES: true, CRYPTO_BACKEND: openssl, CRYPTO_LIB_INSTALL: openssl, SHARED_LIBS: on }
- - { CIRRUS_CLONE_SUBMODULES: true, CRYPTO_BACKEND: botan, CRYPTO_LIB_INSTALL: botan2, SHARED_LIBS: on }
- - { CIRRUS_CLONE_SUBMODULES: true, CRYPTO_BACKEND: botan, CRYPTO_LIB_INSTALL: botan2, SHARED_LIBS: off }
+ - { CIRRUS_CLONE_SUBMODULES: true, CRYPTO_BACKEND: openssl, CRYPTO_LIB_INSTALL: openssl, SHARED_LIBS: on, RNP_LOG_CONSOLE: 1 }
+ - { CIRRUS_CLONE_SUBMODULES: true, CRYPTO_BACKEND: botan, CRYPTO_LIB_INSTALL: botan2, SHARED_LIBS: on, RNP_LOG_CONSOLE: 1 }
+ - { CIRRUS_CLONE_SUBMODULES: true, CRYPTO_BACKEND: botan, CRYPTO_LIB_INSTALL: botan2, SHARED_LIBS: off, RNP_LOG_CONSOLE: 1 }
dependencies_script: |
pkg install -y gcc cmake pkgconf googletest gnupg $CRYPTO_LIB_INSTALL json-c rubygem-asciidoctor
diff --git a/.github/workflows/centos-and-fedora.yml b/.github/workflows/centos-and-fedora.yml
index ad4e6c1..6577da3 100644
--- a/.github/workflows/centos-and-fedora.yml
+++ b/.github/workflows/centos-and-fedora.yml
@@ -1,5 +1,4 @@
name: centos-and-fedora
-
on:
push:
branches:
@@ -34,162 +33,208 @@ concurrency:
cancel-in-progress: true
env:
+ CORES: 2
+ RNP_LOG_CONSOLE: 1
CODECOV_TOKEN: dbecf176-ea3f-4832-b743-295fd71d0fad
-#
-# Dependencies that are created during packaging
-#
-# OS botan botan repository json-c json-c repository
-# ----------------------------------------------------------------------------
-# CentOS 7 2.16.0 ribose json-c12 (0.12.1) ribose
-# CentOS 8 2.16.0 ribose 0.13.1 el8
-# CentOS 9 2.19.3 el9 0.14 el9
-# Fedora 35 2.18.2 fc35 0.15 fc35
-# Fedora 36 2.19.1 fc36 0.15 fc36
-#
jobs:
tests:
+ name: ${{ matrix.image.name }} [CC ${{ matrix.env.CC }}; backend ${{ matrix.image.backend }} ${{ matrix.image.botan_ver }}; gpg ${{ matrix.image.gpg_ver }}; build ${{ matrix.env.BUILD_MODE }}; SM2 ${{ matrix.image.sm2 }}; IDEA ${{ matrix.image.idea }}]
runs-on: ubuntu-latest
- if: "!contains(github.event.head_commit.message, 'skip ci')"
- container: ${{ matrix.image.container }}
- timeout-minutes: 70
+ timeout-minutes: 120
strategy:
fail-fast: false
matrix:
env:
- - { CC: gcc, CXX: g++, BUILD_MODE: normal, USE_STATIC_DEPENDENCIES: yes }
+ - { CC: gcc, CXX: g++, BUILD_MODE: normal, SHARED_LIBS: on }
# normal --> Release build; sanitize --> Debug build so theoretically test conditions are different
-# - { CC: clang, CXX: clang++, BUILD_MODE: normal, USE_STATIC_DEPENDENCIES: yes }
- - { CC: clang, CXX: clang++, BUILD_MODE: sanitize, USE_STATIC_DEPENDENCIES: yes }
-
-# Should you add a new OS/version please consider adding its default version of botan2 and json-c to this test matrix
+# - { CC: clang, CXX: clang++, BUILD_MODE: normal }
+ - { CC: clang, CXX: clang++, BUILD_MODE: sanitize, SHARED_LIBS: on }
+
+# All cotainers have gpg stable and lts installed
+# centos-8-amd64 has botan 2.18.2 installed
+# fedora-35-amd64 has botan 3.1.1 installed
+# Any other version has to be built explicitly !
+# Pls refer to https://github.com/rnpgp/rnp-ci-containers#readme for more image details
image:
- - { name: 'CentOS 7', container: 'centos:7', gpg_ver: stable, backend: Botan, botan_ver: 2.16.0, locale: en_US.UTF-8 }
- - { name: 'CentOS 8', container: 'tgagor/centos:stream8', gpg_ver: stable, backend: Botan, botan_ver: 2.16.0, locale: C.UTF-8 }
- - { name: 'CentOS 9', container: 'quay.io/centos/centos:stream9', gpg_ver: stable, backend: Botan, botan_ver: 2.19.3, locale: C.UTF-8 }
- - { name: 'Fedora 35', container: 'fedora:35', gpg_ver: stable, backend: Botan, botan_ver: 2.18.2, locale: C.UTF-8 }
- - { name: 'Fedora 36', container: 'fedora:36', gpg_ver: stable, backend: Botan, botan_ver: 2.19.1, locale: C.UTF-8 }
- - { name: 'CentOS 8', container: 'tgagor/centos:stream8', gpg_ver: lts, backend: Botan, sm2: On, locale: C.UTF-8 }
- - { name: 'CentOS 8', container: 'tgagor/centos:stream8', gpg_ver: stable, backend: Botan, sm2: Off, locale: C.UTF-8 }
- - { name: 'CentOS 8', container: 'tgagor/centos:stream8', gpg_ver: lts, backend: OpenSSL, locale: C.UTF-8 }
- - { name: 'CentOS 8', container: 'tgagor/centos:stream8', gpg_ver: beta, backend: Botan, sm2: On, locale: C.UTF-8 }
- - { name: 'CentOS 8', container: 'tgagor/centos:stream8', gpg_ver: 2.3.1, backend: Botan, sm2: On, locale: C.UTF-8 }
- - { name: 'CentOS 9', container: 'quay.io/centos/centos:stream9', gpg_ver: stable, backend: OpenSSL, idea: On, locale: C.UTF-8 }
- - { name: 'CentOS 9', container: 'quay.io/centos/centos:stream9', gpg_ver: stable, backend: OpenSSL, idea: Off, locale: C.UTF-8 }
- - { name: 'Fedora 35', container: 'fedora:35', gpg_ver: stable, backend: OpenSSL, locale: C.UTF-8 }
- - { name: 'Fedora 36', container: 'fedora:36', gpg_ver: stable, backend: OpenSSL, locale: C.UTF-8 }
-
-
+ - { name: 'CentOS 7', container: 'centos-7-amd64', backend: 'Botan', botan_ver: 'system', gpg_ver: 'stable' }
+ - { name: 'CentOS 8', container: 'centos-8-amd64', backend: 'Botan', botan_ver: 'system', gpg_ver: 'system' }
+ - { name: 'CentOS 8', container: 'centos-8-amd64', backend: 'Botan', botan_ver: '2.18.2', sm2: On, gpg_ver: 'lts' }
+ - { name: 'CentOS 8', container: 'centos-8-amd64', backend: 'Botan', botan_ver: '2.18.2', sm2: Off, gpg_ver: 'stable' }
+ - { name: 'CentOS 9', container: 'centos-9-amd64', backend: 'Botan', botan_ver: 'system', gpg_ver: 'stable' }
+ - { name: 'Fedora 35', container: 'fedora-35-amd64', backend: 'Botan', botan_ver: 'system', gpg_ver: 'system' }
+ - { name: 'Fedora 36', container: 'fedora-36-amd64', backend: 'Botan', botan_ver: 'system', gpg_ver: 'system' }
+ - { name: 'Fedora 36', container: 'fedora-36-amd64', backend: 'Botan', botan_ver: '3.1.1', gpg_ver: 'system' }
+# Tests against gpg head fails
+# - { name: 'Fedora 36', container: 'fedora-36-amd64', backend: 'Botan', botan_ver: 'system', gpg_ver: 'head' }
+ - { name: 'Fedora 36', container: 'fedora-36-amd64', backend: 'Botan', botan_ver: 'head', gpg_ver: 'system' }
+ - { name: 'CentOS 8', container: 'centos-8-amd64', backend: 'OpenSSL', gpg_ver: 'lts' }
+ - { name: 'CentOS 9', container: 'centos-9-amd64', backend: 'OpenSSL', idea: On, gpg_ver: 'stable' }
+ - { name: 'CentOS 9', container: 'centos-9-amd64', backend: 'OpenSSL', idea: Off,gpg_ver: 'stable' }
+ - { name: 'Fedora 35', container: 'fedora-35-amd64', backend: 'OpenSSL', gpg_ver: 'system' }
+ - { name: 'Fedora 36', container: 'fedora-36-amd64', backend: 'OpenSSL', gpg_ver: 'system' }
+
+# There is some ABI incompatibility between llvm-7, bitan shared library from ribose repo and sanitizer
+# So we are enforving static lib for sanitizers on CentOS 7
+ exclude:
+ - image: { name: 'CentOS 7', container: 'centos-7-amd64', gpg_ver: stable, backend: Botan, botan_ver: 'system' }
+ env: { CC: clang, CXX: clang++, BUILD_MODE: sanitize, SHARED_LIBS: on }
include:
- # Coverage report for Botan backend
- - image: { name: 'CentOS 8', container: 'tgagor/centos:stream8', gpg_ver: stable, backend: Botan, sm2: On, locale: C.UTF-8 }
- env: { CC: gcc, CXX: g++, BUILD_MODE: coverage , RNP_TESTS: ".*", USE_STATIC_DEPENDENCIES: yes }
+ - image: { name: 'CentOS 7', container: 'centos-7-amd64', gpg_ver: stable, backend: Botan, botan_ver: 'system' }
+ env: { CC: clang, CXX: clang++, BUILD_MODE: sanitize, SHARED_LIBS: off }
+ # Coverage report for Botan 2.x backend
+ - image: { name: 'CentOS 8', container: 'centos-8-amd64', gpg_ver: stable, backend: Botan, botan_ver: '2.18.2' }
+ env: { CC: gcc, CXX: g++, BUILD_MODE: coverage, SHARED_LIBS: on }
+ # Coverage report for Botan 3.x backend
+ - image: { name: 'Fedora 36', container: 'fedora-36-amd64', gpg_ver: stable, backend: Botan, botan_ver: '3.1.1' }
+ env: { CC: gcc, CXX: g++, BUILD_MODE: coverage, SHARED_LIBS: on }
# Coverage report for OpenSSL 1.1.1 backend
- - image: { name: 'CentOS 8', container: 'tgagor/centos:stream8', gpg_ver: stable, backend: OpenSSL, locale: C.UTF-8 }
- env: { CC: gcc, CXX: g++, BUILD_MODE: coverage , RNP_TESTS: ".*", USE_STATIC_DEPENDENCIES: yes }
+ - image: { name: 'CentOS 8', container: 'centos-8-amd64', gpg_ver: stable, backend: OpenSSL }
+ env: { CC: gcc, CXX: g++, BUILD_MODE: coverage, SHARED_LIBS: on }
# Coverage report for OpenSSL 3.0 backend
- - image: { name: 'Fedora 36', container: 'fedora:36', gpg_ver: stable, backend: OpenSSL, locale: C.UTF-8 }
- env: { CC: gcc, CXX: g++, BUILD_MODE: coverage , RNP_TESTS: ".*", USE_STATIC_DEPENDENCIES: yes }
+ - image: { name: 'Fedora 36', container: 'fedora-36-amd64', gpg_ver: stable, backend: OpenSSL }
+ env: { CC: gcc, CXX: g++, BUILD_MODE: coverage, SHARED_LIBS: on }
+ # Coverage report for OpenSSL 3.0 backend with disabled algos
+ - image: { name: 'Fedora 36', container: 'fedora-36-amd64', gpg_ver: stable, backend: OpenSSL, idea: Off, sm2: Off, two: Off, blow: Off, rmd: Off, bp: Off }
+ env: { CC: gcc, CXX: g++, BUILD_MODE: coverage, SHARED_LIBS: on }
+ # Coverage report for Botan backend with disabled algos
+ - image: { name: 'Fedora 36', container: 'fedora-36-amd64', gpg_ver: stable, backend: Botan, idea: Off, sm2: Off, two: Off, blow: Off, rmd: Off, bp: Off }
+ env: { CC: gcc, CXX: g++, BUILD_MODE: coverage, SHARED_LIBS: on }
+ # Fedora 38
+ - image: { name: 'Fedora 38', container: 'fedora-38-amd64', gpg_ver: system, backend: Botan, botan_ver: 'system' }
+ env: { CC: gcc, CXX: g++, BUILD_MODE: normal, SHARED_LIBS: on }
+ # Fedora 39
+ - image: { name: 'Fedora 38', container: 'fedora-39-amd64', gpg_ver: system, backend: Botan, botan_ver: 'system' }
+ env: { CC: gcc, CXX: g++, BUILD_MODE: normal, SHARED_LIBS: on }
+
+ container: ghcr.io/rnpgp/ci-rnp-${{ matrix.image.container }}
env: ${{ matrix.env }}
- name: ${{ matrix.image.name }} ${{ matrix.image.backend }} [test mode ${{ matrix.env.BUILD_MODE }}; CC ${{ matrix.env.CC }}; GnuPG ${{ matrix.image.gpg_ver }}; SM2 ${{ matrix.image.sm2 }}; IDEA ${{ matrix.image.idea }}]
steps:
- - name: Install prerequisites for prerequisites
- if: matrix.image.container == 'centos:7'
- run: yum -y install http://opensource.wandisco.com/centos/7/git/x86_64/wandisco-git-release-7-2.noarch.rpm
-
- - name: Install prerequisites
- run: yum -y install git sudo
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ submodules: true
- name: Setup environment
run: |
set -o errexit -o pipefail -o noclobber -o nounset
- echo LANG=${{ matrix.image.locale }} >> $GITHUB_ENV
- echo LC_ALL=${{ matrix.image.locale }} >> $GITHUB_ENV
- echo LC_LANG=${{ matrix.image.locale }} >> $GITHUB_ENV
- echo GPG_VERSION=${{ matrix.image.gpg_ver }} >> $GITHUB_ENV
- echo ENABLE_SM2=${{ matrix.image.sm2 }} >> $GITHUB_ENV
- echo ENABLE_IDEA=${{ matrix.image.idea }} >> $GITHUB_ENV
- backend=${{ matrix.image.backend }}
- backend="$(echo "${backend:-}" | tr '[:upper:]' '[:lower:]')"
- echo CRYPTO_BACKEND="$backend" >> $GITHUB_ENV
- echo BOTAN_VERSION=${{ matrix.image.botan_ver }} >> $GITHUB_ENV
+
+ /opt/tools/tools.sh select_crypto_backend_for_gha ${{ matrix.image.backend }}
+ /opt/tools/tools.sh select_gpg_version_for_gha ${{ matrix.image.gpg_ver }}
+ /opt/tools/tools.sh select_botan_version_for_gha ${{ matrix.image.botan_ver }}
+
+ echo "ENABLE_SM2=${{ matrix.image.sm2 }}" >> $GITHUB_ENV
+ echo "ENABLE_IDEA=${{ matrix.image.idea }}" >> $GITHUB_ENV
+ echo "ENABLE_TWOFISH=${{ matrix.image.two }}" >> $GITHUB_ENV
+ echo "ENABLE_BLOWFISH=${{ matrix.image.blow }}" >> $GITHUB_ENV
+ echo "ENABLE_RIPEMD160=${{ matrix.image.rmd }}" >> $GITHUB_ENV
+ echo "ENABLE_BRAINPOOL=${{ matrix.image.bp }}" >> $GITHUB_ENV
+
+ echo CORES="$(nproc --all)" >> $GITHUB_ENV
+
useradd rnpuser
- echo -e "rnpuser\tALL=(ALL)\tNOPASSWD:\tALL" > /etc/sudoers.d/rnpuser
- echo -e "rnpuser\tsoft\tnproc\tunlimited\n" > /etc/security/limits.d/30-rnpuser.conf
+ printf "\nrnpuser\tALL=(ALL)\tNOPASSWD:\tALL" > /etc/sudoers.d/rnpuser
+ printf "\nrnpuser\tsoft\tnproc\tunlimited\n" > /etc/security/limits.d/30-rnpuser.conf
- - name: Checkout
- uses: actions/checkout@v3
- with:
- submodules: true
+ # Need to build HEAD version since it is always different
+ - name: Build gpg head
+ if: matrix.image.gpg_ver == 'head'
+ run: /opt/tools/tools.sh build_and_install_gpg head
- - name: Setup noncacheable dependencies
+ - name: Build botan head
+ if: matrix.image.botan_ver == 'head'
+ # Botan's head renamed curve25519 module to x25519, however this didn't get to 3.5.0 release yet
run: |
- . ci/gha/setup-env.inc.sh
- exec su rnpuser -c ci/install_noncacheable_dependencies.sh
+ sed -i 's/curve25519/x25519/g' /opt/tools/botan3-modules /opt/tools/botan3-pqc-modules
+ /opt/tools/tools.sh build_and_install_botan head
- - name: Cache
- id: cache
- uses: actions/cache@v3
- with:
- path: ${{ env.CACHE_DIR }}
- key: ${{ matrix.image.container }}-${{ matrix.image.backend }}-${{ matrix.env.BUILD_MODE }}-${{ matrix.env.CC }}-${{ matrix.image.gpg_ver }}-${{ matrix.image.sm2 }}-${{ matrix.image.idea }}-${{ hashFiles('ci/**') }}-${{ hashFiles('.github/workflows/centos-and-fedora.yml') }}
-
- - name: Adjust folder ownership
+ - name: Configure
run: |
set -o errexit -o pipefail -o noclobber -o nounset
- chown -R rnpuser:rnpuser $PWD
- - name: Setup cacheable dependencies
- if: steps.cache.outputs.cache-hit != 'true'
- run: exec su rnpuser -c ci/install_cacheable_dependencies.sh
+ [[ "${{ env.BUILD_MODE }}" = "coverage" ]] && cov_opt=(-DENABLE_COVERAGE=yes)
+ [[ "${{ env.BUILD_MODE }}" = "sanitize" ]] && san_opt=(-DENABLE_SANITIZERS=yes)
- - name: Build and Test
- run: exec su rnpuser -c ci/run.sh
+ [ -n "$ENABLE_SM2" ] && sm2_opt=(-DENABLE_SM2="$ENABLE_SM2")
+ [ -n "$ENABLE_IDEA" ] && idea_opt=(-DENABLE_IDEA="$ENABLE_IDEA")
+ [ -n "$ENABLE_TWOFISH" ] && two_opt=(-DENABLE_TWOFISH="$ENABLE_TWOFISH")
+ [ -n "$ENABLE_BLOWFISH" ] && blow_opt=(-DENABLE_BLOWFISH="$ENABLE_BLOWFISH")
+ [ -n "$ENABLE_RIPEMD160" ] && rmd_opt=(-DENABLE_RIPEMD160="$ENABLE_RIPEMD160")
+ [ -n "$ENABLE_BRAINPOOL" ] && bp_opt=(-DENABLE_BRAINPOOL="$ENABLE_BRAINPOOL")
+
+ cmake -B build \
+ -DBUILD_SHARED_LIBS=${{ env.SHARED_LIBS }} \
+ -DDOWNLOAD_GTEST=ON \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DCRYPTO_BACKEND=${{ matrix.image.backend }} \
+ ${sm2_opt:-} ${idea_opt:-} ${two_opt:-} ${blow_opt:-} ${rmd_opt:-} ${bp_opt:-} ${cov_opt:-} ${san_opt:-} .
+
+ - name: Build
+ run: cmake --build build --parallel ${{ env.CORES }}
+
+ - name: Test
+ run: |
+ mkdir -p "build/Testing/Temporary"
+ cp "cmake/CTestCostData.txt" "build/Testing/Temporary"
+ export PATH="$PWD/build/src/lib:$PATH"
+ chown -R rnpuser:rnpuser $PWD
+ exec su rnpuser -c "ctest --parallel ${{ env.CORES }} --test-dir build --output-on-failure"
+
+ - name: Coverage
+ if: env.BUILD_MODE == 'coverage'
+ run: |
+ curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import # One-time step
+ curl -Os https://uploader.codecov.io/latest/linux/codecov
+ curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM
+ curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig
+ gpgv codecov.SHA256SUM.sig codecov.SHA256SUM
+ shasum -a 256 -c codecov.SHA256SUM
+ chmod +x codecov
+ find "build" -type f -name '*.gcno' -exec gcov -p {} +
+ ./codecov
+
+ - name: Install
+ if: env.BUILD_MODE != 'coverage' && env.SHARED_LIBS == 'on'
+ run: cmake --install build
- name: Checkout shell test framework
+ if: env.BUILD_MODE != 'coverage' && env.SHARED_LIBS == 'on'
uses: actions/checkout@v3
with:
repository: kward/shunit2
path: ci/tests/shunit2
- name: Run additional ci tests
- run: ci/tests/ci-tests.sh
+ if: env.BUILD_MODE != 'coverage' && env.SHARED_LIBS == 'on'
+ run: RNP_INSTALL=/usr/local ci/tests/ci-tests.sh
package-source:
runs-on: ubuntu-latest
- container: ${{ matrix.env.container }}
+ container: ghcr.io/rnpgp/ci-rnp-${{ matrix.image.container }}
timeout-minutes: 30
+# needs: tests
strategy:
fail-fast: false
matrix:
- env:
- - { name: 'CentOS 7', container: 'centos:7', LC_ALL: en_US.UTF-8 }
- - { name: 'CentOS 8', container: 'tgagor/centos:stream8', LC_ALL: C.UTF-8 }
- - { name: 'CentOS 9', container: 'quay.io/centos/centos:stream9', LC_ALL: C.UTF-8 }
- - { name: 'Fedora 35', container: 'fedora:35', LC_ALL: C.UTF-8 }
- - { name: 'Fedora 36', container: 'fedora:36', LC_ALL: C.UTF-8 }
- name: Package ${{ matrix.env.name }} SRPM
- env: ${{ matrix.env }}
+ image:
+ - { name: 'CentOS 7', container: 'centos-7-amd64' }
+ - { name: 'CentOS 8', container: 'centos-8-amd64' }
+ - { name: 'CentOS 9', container: 'centos-9-amd64' }
+ - { name: 'Fedora 35', container: 'fedora-35-amd64' }
+ - { name: 'Fedora 36', container: 'fedora-36-amd64' }
- steps:
- - name: Install prerequisites for prerequisites
- if: matrix.env.container == 'centos:7'
- run: yum -y install http://opensource.wandisco.com/centos/7/git/x86_64/wandisco-git-release-7-2.noarch.rpm
+ name: Package ${{ matrix.image.name }} SRPM
- - name: Install prerequisites
- run: yum -y install git sudo rpm-build
+ steps:
+ - name: Install rpm tools
+ run: yum -y install rpm-build
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
- - name: Setup noncacheable dependencies
- run: |
- . ci/gha/setup-env.inc.sh
- ci/install_noncacheable_dependencies.sh
-
- name: Configure
run: cmake -B build -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF
@@ -199,7 +244,7 @@ jobs:
- name: Upload SRPM
uses: actions/upload-artifact@v3
with:
- name: 'SRPM ${{ matrix.env.name }}'
+ name: 'SRPM ${{ matrix.image.name }}'
path: 'build/SRPM/*.src.rpm'
retention-days: 5
@@ -212,34 +257,28 @@ jobs:
package:
runs-on: ubuntu-latest
- needs: package-source
- container: ${{ matrix.env.container }}
+ container: ghcr.io/rnpgp/ci-rnp-${{ matrix.image.container }}
timeout-minutes: 30
+ needs: package-source
strategy:
fail-fast: false
matrix:
- env:
- - { name: 'CentOS 7', container: 'centos:7', LC_ALL: en_US.UTF-8 }
-# CXXFLAGS environment setting resolves dual ABI issues caused by BOTAN libraries with the version of GCC installed at 'tgagor/centos:stream8'
-# https://gcc.gnu.org/onlinedocs/gcc-5.2.0/libstdc++/manual/manual/using_dual_abi.html
- - { name: 'CentOS 8', container: 'tgagor/centos:stream8', CXXFLAGS: -D_GLIBCXX_USE_CXX11_ABI=0, LC_ALL: C.UTF-8 }
- - { name: 'CentOS 9', container: 'quay.io/centos/centos:stream9', LC_ALL: C.UTF-8 }
- - { name: 'Fedora 35', container: 'fedora:35', LC_ALL: C.UTF-8 }
- - { name: 'Fedora 36', container: 'fedora:36', LC_ALL: C.UTF-8 }
- name: Package ${{ matrix.env.name }} RPM
- env: ${{ matrix.env }}
- steps:
- - name: Install prerequisites for prerequisites
- if: matrix.env.container == 'centos:7'
- run: yum -y install http://opensource.wandisco.com/centos/7/git/x86_64/wandisco-git-release-7-2.noarch.rpm
+ image:
+ - { name: 'CentOS 7', container: 'centos-7-amd64' }
+ - { name: 'CentOS 8', container: 'centos-8-amd64' }
+ - { name: 'CentOS 9', container: 'centos-9-amd64' }
+ - { name: 'Fedora 35', container: 'fedora-35-amd64' }
+ - { name: 'Fedora 36', container: 'fedora-36-amd64' }
- - name: Install prerequisites
- run: yum -y install git sudo tar cpio rpm-build
+ name: Package ${{ matrix.image.name }} RPM
+ steps:
+ - name: Install rpm tools
+ run: yum -y install rpm-build
- name: Download SRPM
uses: actions/download-artifact@v3
with:
- name: 'SRPM ${{ matrix.env.name }}'
+ name: 'SRPM ${{ matrix.image.name }}'
path: ~/rpmbuild/SRPMS
- name: Extract SRPM
@@ -247,16 +286,6 @@ jobs:
rpm -i -v ~/rpmbuild/SRPMS/*.src.rpm
tar xzf ~/rpmbuild/SOURCES/*.tar.gz --strip 1 -C ~/rpmbuild/SOURCES
- - name: Setup noncacheable dependencies
- run: |
- cd ~/rpmbuild/SOURCES/
- . ci/gha/setup-env.inc.sh
- ci/install_noncacheable_dependencies.sh
-
- - name: Permanently enable rh-ruby30
- if: matrix.env.container == 'centos:7'
- run: bash -c "echo \"$(cut -f 2- -d ' ' /opt/rh/rh-ruby30/enable)\"">> $GITHUB_ENV
-
- name: Build rnp
run: |
cmake ~/rpmbuild/SOURCES -B ~/rpmbuild/SOURCES/BUILD -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF \
@@ -269,7 +298,7 @@ jobs:
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
- name: 'RPM ${{ matrix.env.name}}'
+ name: 'RPM ${{ matrix.image.name}}'
path: '~/rpmbuild/SOURCES/RPMS/*.rpm'
retention-days: 5
@@ -280,18 +309,18 @@ jobs:
rpm-tests:
runs-on: ubuntu-latest
needs: package
- container: ${{ matrix.env.container }}
+ container: ${{ matrix.image.container }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
- env:
+ image:
- { name: 'CentOS 7', container: 'centos:7' }
- { name: 'CentOS 8', container: 'tgagor/centos:stream8' }
- { name: 'CentOS 9', container: 'quay.io/centos/centos:stream9' }
- { name: 'Fedora 35', container: 'fedora:35' }
- { name: 'Fedora 36', container: 'fedora:36' }
- name: RPM test on ${{ matrix.env.name }}
+ name: RPM test on ${{ matrix.image.name }}
steps:
- name: Install prerequisites
@@ -303,26 +332,26 @@ jobs:
# ribose repo is also a source of json-c (v12 aka json-c12) for CentOS 7
- name: Install ribose-packages
- if: matrix.env.container == 'centos:7' || matrix.env.container == 'tgagor/centos:stream8'
+ if: matrix.image.container == 'centos:7' || matrix.image.container == 'tgagor/centos:stream8'
run: |
sudo rpm --import https://github.com/riboseinc/yum/raw/master/ribose-packages-next.pub
sudo wget https://github.com/riboseinc/yum/raw/master/ribose.repo -O /etc/yum.repos.d/ribose.repo
- name: Install epel-release
- if: matrix.env.container == 'quay.io/centos/centos:stream9'
+ if: matrix.image.container == 'quay.io/centos/centos:stream9'
run: |
sudo dnf -y install 'dnf-command(config-manager)'
sudo dnf config-manager --set-enabled crb
sudo dnf -y install epel-release
- name: Install xargs
- if: matrix.env.container == 'fedora:35'
+ if: matrix.image.container == 'fedora:35'
run: sudo yum -y install findutils
- name: Download rnp rpms
uses: actions/download-artifact@v3
with:
- name: 'RPM ${{ matrix.env.name}}'
+ name: 'RPM ${{ matrix.image.name}}'
- name: Checkout shell test framework
uses: actions/checkout@v3
@@ -362,11 +391,11 @@ jobs:
# Ribose repo provides json-c12-devel for CentOS7;
# el8, el9, fr35, fr36 provide json-c-devel (version 12+)
- name: Setup json-c12
- if: matrix.env.container == 'centos:7'
+ if: matrix.image.container == 'centos:7'
run: sudo yum -y install json-c12-devel
- name: Setup json-c
- if: matrix.env.container != 'centos:7'
+ if: matrix.image.container != 'centos:7'
run: sudo yum -y install json-c-devel
- name: Run packaging tests
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
index 35f1ea5..3e16728 100644
--- a/.github/workflows/coverity.yml
+++ b/.github/workflows/coverity.yml
@@ -5,13 +5,6 @@ on:
# every day at 9:00 UTC
- cron: '0 9 * * *'
-env:
- CORES: 2
- BUILD_MODE: normal
- GPG_VERSION: stable
- RNP_TESTS: ''
- USE_STATIC_DEPENDENCIES: yes
-
jobs:
scan:
runs-on: ubuntu-latest
@@ -21,43 +14,23 @@ jobs:
with:
fetch-depth: 1
submodules: true
- - name: Setup environment
- run: |
- . ci/gha/setup-env.inc.sh
- ci/install_noncacheable_dependencies.sh
- - name: Cache
- id: cache
- uses: actions/cache@v3
- with:
- path: ${{ env.CACHE_DIR }}
- key: ${{ github.workflow }}-${{ runner.os }}-${{ env.BUILD_MODE }}-gpg-${{ env.GPG_VERSION }}-${{ hashFiles('ci/**') }}-${{ hashFiles('.github/workflows/**') }}
- - name: Build cache
- if: steps.cache.outputs.cache-hit != 'true'
- run: |
- set -x
- ci/install_cacheable_dependencies.sh botan jsonc
- - name: Download Coverity
- env:
- TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
- run: |
- wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$TOKEN&project=$GITHUB_REPOSITORY" -O cov-analysis-linux64.tar.gz
- mkdir cov-analysis-linux64
- tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
- - name: Build
+
+ - name: Install dependencies
run: |
- set -x
- export PATH="$PWD/cov-analysis-linux64/bin:$PATH"
- cov-build --dir cov-int ci/main.sh
- - name: Submit
- env:
- TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
+ sudo apt-get -y update
+ sudo apt-get -y install cmake libjson-c-dev libbotan-2-dev asciidoctor
+
+ - name: Configure
run: |
- tar czvf results.tgz cov-int
- curl \
- --form project=$GITHUB_REPOSITORY \
- --form token=$TOKEN \
- --form email=packaging@ribose.com \
- --form file=@results.tgz \
- --form version=$GITHUB_REF \
- --form description=$GITHUB_SHA \
- https://scan.coverity.com/builds?project=$GITHUB_REPOSITORY
+ echo CORES="$(nproc --all)" >> $GITHUB_ENV
+ cmake -B build -DBUILD_SHARED_LIBS=ON \
+ -DCRYPTO_BACKEND=botan \
+ -DDOWNLOAD_GTEST=ON \
+ -DCMAKE_BUILD_TYPE=Release .
+
+ - name: Coverity Scan
+ uses: vapier/coverity-scan-action@v1
+ with:
+ email: packaging@ribose.com
+ token: ${{ secrets.COVERITY_SCAN_TOKEN }}
+ command: cmake --build build --parallel $CORES
diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml
index 30991fc..e5bcc97 100644
--- a/.github/workflows/debian.yml
+++ b/.github/workflows/debian.yml
@@ -38,16 +38,11 @@ env:
LANG: C.UTF-8
LC_ALL: C.UTF-8
LC_LANG: C.UTF-8
- CMAKE_VER: '3.20.6-2'
- BUILD_MODE: normal
- GPG_VERSION: stable
- SUDO: ""
- USE_STATIC_DEPENDENCIES: yes
RNP_LOG_CONSOLE: 1
jobs:
tests:
- name: ${{ matrix.image.container }} [CC ${{ matrix.env.CC }}; backend ${{ matrix.image.backend }}; GnuPG stable]
+ name: ${{ matrix.image.container }} [CC ${{ matrix.env.CC }}; backend ${{ matrix.image.backend }}; GnuPG system-shipped]
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'skip ci')"
timeout-minutes: 120
@@ -55,84 +50,59 @@ jobs:
fail-fast: false
matrix:
image:
- - { container: 'i386/debian:11', cpu: 'i386', arch: 'ia32', backend: 'botan' }
- - { container: 'i386/debian:11', cpu: 'i386', arch: 'ia32', backend: 'openssl' }
- - { container: 'amd64/debian:11', cpu: 'x86_64', arch: 'x64', backend: 'botan' }
- - { container: 'amd64/debian:11', cpu: 'x86_64', arch: 'x64', backend: 'openssl' }
- - { container: 'i386/debian:10', cpu: 'i386', arch: 'ia32', backend: 'botan' }
+ - { container: 'debian-11-i386', cpu: 'i386', backend: 'botan' }
+ - { container: 'debian-11-i386', cpu: 'i386', backend: 'openssl' }
+ - { container: 'debian-11-amd64', cpu: 'x86_64', backend: 'botan' }
+ - { container: 'debian-11-amd64', cpu: 'x86_64', backend: 'openssl' }
+ - { container: 'debian-12-amd64', cpu: 'x86_64', backend: 'botan' }
+ - { container: 'debian-12-amd64', cpu: 'x86_64', backend: 'openssl' }
+ - { container: 'debian-10-i386', cpu: 'i386', backend: 'botan' }
env:
- { CC: 'gcc', CXX: 'g++' }
- { CC: 'clang', CXX: 'clang++' }
- container: ${{ matrix.image.container }}
+ container: ghcr.io/rnpgp/ci-rnp-${{ matrix.image.container }}
env: ${{ matrix.env }}
steps:
- - name: Install prerequisites
- run: |
- apt update
- apt -y install git sudo wget
-
- - name: Setup environment
- shell: bash
- # rnpuser is only needed for rnpkeys_generatekey_verifykeyHomeDirNoPermission test
- run: |
- set -x
- echo IMAGE=${{ matrix.image.container }} >> $GITHUB_ENV
- echo CPU=${{ matrix.image.cpu }} >> $GITHUB_ENV
- echo CRYPTO_BACKEND=${{ matrix.image.backend }} >> $GITHUB_ENV
- echo "SUDO=sudo" >> $GITHUB_ENV
- useradd rnpuser
- printf "\nrnpuser\tALL=(ALL)\tNOPASSWD:\tALL" > /etc/sudoers.d/rnpuser
- printf "\nrnpuser\tsoft\tnproc\tunlimited\n" > /etc/security/limits.d/30-rnpuser.conf
-
- name: Checkout on x86_x64
- if: env.CPU == 'x86_64'
+ if: matrix.image.cpu == 'x86_64'
uses: actions/checkout@v3
with:
submodules: true
- name: Checkout on i386
- if: env.CPU == 'i386'
+ if: matrix.image.cpu == 'i386'
uses: actions/checkout@v1
with:
submodules: true
- - name: Install cmake
- run: |
- wget -nv https://github.com/xpack-dev-tools/cmake-xpack/releases/download/v${{ env.CMAKE_VER }}/xpack-cmake-${{ env.CMAKE_VER }}-linux-${{ matrix.image.arch }}.tar.gz
- tar -zxf xpack-cmake-${{ env.CMAKE_VER }}-linux-${{ matrix.image.arch }}.tar.gz --directory /usr/local --strip-components=1 --skip-old-files
-
- - name: Setup noncacheable dependencies
+ - name: Setup environment
shell: bash
+ # rnpuser is only needed for rnpkeys_generatekey_verifykeyHomeDirNoPermission test
run: |
- . ci/gha/setup-env.inc.sh
- ci/install_noncacheable_dependencies.sh
-
- - name: Cache
- id: cache
- uses: actions/cache@v3
- if: env.CPU == 'x86_64'
- with:
- path: ${{github.workspace}}/${{ env.CACHE_DIR }}
- key: ${{ matrix.image.container }}-${{ matrix.env.CC }}-${{ matrix.image.backend }}-${{ hashFiles('ci/**') }}-${{ hashFiles('.github/workflows/debian.yml') }}
+ useradd rnpuser
+ printf "\nrnpuser\tALL=(ALL)\tNOPASSWD:\tALL" > /etc/sudoers.d/rnpuser
+ printf "\nrnpuser\tsoft\tnproc\tunlimited\n" > /etc/security/limits.d/30-rnpuser.conf
- - name: Setup cacheable dependencies
- if: steps.cache.outputs.cache-hit != 'true'
- shell: bash
+ - name: Configure
run: |
- set -euxo pipefail
- ci/install_cacheable_dependencies.sh
+ cmake -B build \
+ -DBUILD_SHARED_LIBS=ON \
+ -DCRYPTO_BACKEND=${{ matrix.image.backend }} \
+ -DDOWNLOAD_GTEST=ON \
+ -DCMAKE_BUILD_TYPE=Release .
- - name: Build and Test
- shell: bash
+ - name: Build
+ run: cmake --build build --parallel ${{ env.CORES }}
+
+ - name: Test
run: |
- set -x
+ mkdir -p "build/Testing/Temporary"
+ cp "cmake/CTestCostData.txt" "build/Testing/Temporary"
+ export PATH="$PWD/build/src/lib:$PATH"
chown -R rnpuser:rnpuser $PWD
- exec su rnpuser -c ci/run.sh
+ exec su rnpuser -c "ctest --parallel ${{ env.CORES }} --test-dir build --output-on-failure"
- name: Package
- run: |
- set -x
- cd ${LOCAL_BUILDS}/rnp-build
- cpack -G DEB -D CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS="${BOTAN_INSTALL}/lib;${JSONC_INSTALL}/lib;${GPG_INSTALL}/lib"
+ run: cpack -G DEB -B debian --config build/CPackConfig.cmake
diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml
index 81336ec..c51260a 100644
--- a/.github/workflows/fuzzing.yml
+++ b/.github/workflows/fuzzing.yml
@@ -31,7 +31,7 @@ jobs:
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'rnp'
- fuzz-seconds: 1800
+ fuzz-seconds: 300
dry-run: false
- name: Upload Crash
uses: actions/upload-artifact@v2
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 0cfea7e..42aef30 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 [Ribose Inc](https://www.ribose.com).
+# Copyright (c) 2023-2024 [Ribose Inc](https://www.ribose.com).
# All rights reserved.
# This file is a part of rnp
#
@@ -57,7 +57,8 @@ concurrency:
cancel-in-progress: true
env:
- BOTAN_VERSION: 2.19.3
+ BOTAN_VERSION: 2.19.4
+ CORES: 3
jobs:
tests:
@@ -67,13 +68,14 @@ jobs:
fail-fast: false
matrix:
# On MacOS gcc is alias of clang these days
- os: [ macos-11, macos-12 ]
+ os: [ macos-12, macos-13, macos-14 ]
backend: [ 'botan' ]
shared_libs: [ 'on' ]
include:
- { os: 'macos-11', backend: 'openssl@1.1', shared_libs: 'on' }
- - { os: 'macos-12', backend: 'openssl@3', shared_libs: 'on' }
- - { os: 'macos-12', backend: 'botan', shared_libs: 'off' }
+ - { os: 'macos-14', backend: 'openssl@3', shared_libs: 'on' }
+ - { os: 'macos-14', backend: 'botan', shared_libs: 'off' }
+ - { os: 'macos-14', backend: 'botan3', shared_libs: 'on' }
if: "!contains(github.event.head_commit.message, 'skip ci')"
timeout-minutes: 250
@@ -98,14 +100,6 @@ jobs:
echo "OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" >> $GITHUB_ENV
echo "CRYPTO_BACKEND=openssl" >> $GITHUB_ENV
-# Brew installs Botan3 now and it is not supported yet
-#
-# - name: Configure botan backend
-# if: ${{ matrix.backend == 'botan' }}
-# run: |
-# echo "brew \"botan\"" >> Brewfile
-# echo "CRYPTO_BACKEND=botan" >> $GITHUB_ENV
-
- name: Install dependencies
run: brew bundle
@@ -133,6 +127,11 @@ jobs:
sudo make install
cd ..
+ - name: Install Botan3
+ if: matrix.backend == 'botan3'
+ run: |
+ brew install botan
+
- name: Configure
run: |
echo "CORES=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV
@@ -141,6 +140,7 @@ jobs:
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$PWD/rnp-install" \
-DDOWNLOAD_GTEST=OFF \
+ -DCMAKE_CXX_FLAGS="-DS2K_MINIMUM_TUNING_RATIO=4"\
-DCRYPTO_BACKEND=${{ env.CRYPTO_BACKEND }} .
- name: Build
diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml
index e71ee51..c5bbcfd 100644
--- a/.github/workflows/nix.yml
+++ b/.github/workflows/nix.yml
@@ -39,7 +39,7 @@ jobs:
with:
fetch-depth: 1
submodules: true
- - uses: cachix/install-nix-action@v15
+ - uses: cachix/install-nix-action@v22
with:
nix_path: nixpkgs=channel:nixos-unstable
- run: nix build .?submodules=1
diff --git a/.github/workflows/opensuse.yml b/.github/workflows/opensuse.yml
new file mode 100644
index 0000000..d02381c
--- /dev/null
+++ b/.github/workflows/opensuse.yml
@@ -0,0 +1,96 @@
+name: opensuse
+
+on:
+ push:
+ branches:
+ - main
+ - 'release/**'
+ paths-ignore:
+ - '/*.sh'
+ - '/.*'
+ - '/_*'
+ - 'Brewfile'
+ - 'docs/**'
+ - '**.adoc'
+ - '**.md'
+ - '**.nix'
+ - 'flake.lock'
+ - '.github/workflows/*.yml'
+ - '!.github/workflows/opensuse.yml'
+ pull_request:
+ paths-ignore:
+ - '/*.sh'
+ - '/.*'
+ - '/_*'
+ - 'Brewfile'
+ - 'docs/**'
+ - '**.adoc'
+ - '**.md'
+ - '**.nix'
+ - 'flake.lock'
+
+concurrency:
+ group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}'
+ cancel-in-progress: true
+
+env:
+ CORES: 2
+ LANG: C.UTF-8
+ LC_ALL: C.UTF-8
+ LC_LANG: C.UTF-8
+ RNP_LOG_CONSOLE: 1
+
+jobs:
+ tests:
+ name: ${{ matrix.image.container }} [CC ${{ matrix.env.CC }}; backend ${{ matrix.image.backend }}; GnuPG system-shipped]
+ runs-on: ubuntu-latest
+ if: "!contains(github.event.head_commit.message, 'skip ci')"
+ timeout-minutes: 120
+ strategy:
+ fail-fast: false
+ matrix:
+ image:
+ - { container: 'opensuse-leap', backend: 'botan' }
+ - { container: 'opensuse-tumbleweed', backend: 'openssl' }
+ env:
+ - { CC: 'gcc', CXX: 'g++' }
+ - { CC: 'clang', CXX: 'clang++' }
+
+ container: ghcr.io/rnpgp/ci-rnp-${{ matrix.image.container }}
+
+ env: ${{ matrix.env }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ submodules: true
+
+ - name: Setup environment
+ shell: bash
+ # rnpuser is only needed for rnpkeys_generatekey_verifykeyHomeDirNoPermission test
+ run: |
+ groupadd rnpuser
+ useradd -g rnpuser -m rnpuser
+ printf "\nrnpuser\tALL=(ALL)\tNOPASSWD:\tALL" > /etc/sudoers.d/rnpuser
+ if [ -d /etc/security/limits.d ]; then
+ printf "\nrnpuser\tsoft\tnproc\tunlimited\n" > /etc/security/limits.d/30-rnpuser.conf
+ fi
+
+ - name: Configure
+ run: |
+ cmake -B build \
+ -DBUILD_SHARED_LIBS=ON \
+ -DCRYPTO_BACKEND=${{ matrix.image.backend }} \
+ -DDOWNLOAD_GTEST=Off \
+ -DCMAKE_BUILD_TYPE=Release .
+
+ - name: Build
+ run: cmake --build build --parallel ${{ env.CORES }}
+
+ - name: Test
+ run: |
+ mkdir -p "build/Testing/Temporary"
+ cp "cmake/CTestCostData.txt" "build/Testing/Temporary"
+ export PATH="$PWD/build/src/lib:$PATH"
+ chown -R rnpuser:rnpuser $PWD
+ exec su rnpuser -c "ctest --parallel ${{ env.CORES }} --test-dir build --output-on-failure"
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index 51fd0c3..a7931d1 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -110,14 +110,14 @@ jobs:
-DCMAKE_BUILD_TYPE=Release .
- name: Build
- run: cmake --build build --config "Release" --parallel ${{ env.CORES }}
+ run: cmake --build build --parallel ${{ env.CORES }}
- name: Test
run: |
mkdir -p "build/Testing/Temporary"
cp "cmake/CTestCostData.txt" "build/Testing/Temporary"
export PATH="$PWD/build/src/lib:$PATH"
- ctest --parallel ${{ env.CORES }} --test-dir build -C Debug --output-on-failure
+ ctest --parallel ${{ env.CORES }} --test-dir build --output-on-failure
cmake-offline-googletest-src:
runs-on: ubuntu-latest
@@ -145,14 +145,14 @@ jobs:
-DCMAKE_BUILD_TYPE=Release .
- name: Build
- run: cmake --build build --config "Release" --parallel ${{ env.CORES }}
+ run: cmake --build build --parallel ${{ env.CORES }}
- name: Test
run: |
mkdir -p "build/Testing/Temporary"
cp "cmake/CTestCostData.txt" "build/Testing/Temporary"
export PATH="$PWD/build/src/lib:$PATH"
- ctest --parallel ${{ env.CORES }} --test-dir build -C Debug --output-on-failure
+ ctest --parallel ${{ env.CORES }} --test-dir build --output-on-failure
- name: Check googletest
run: |
@@ -191,14 +191,14 @@ jobs:
-DCMAKE_BUILD_TYPE=Release .
- name: Build
- run: cmake --build build --config "Release" --parallel ${{ env.CORES }}
+ run: cmake --build build --parallel ${{ env.CORES }}
- name: Test
run: |
mkdir -p "build/Testing/Temporary"
cp "cmake/CTestCostData.txt" "build/Testing/Temporary"
export PATH="$PWD/build/src/lib:$PATH"
- ctest --parallel ${{ env.CORES }} --test-dir build -C Debug --output-on-failure
+ ctest --parallel ${{ env.CORES }} --test-dir build --output-on-failure
- name: Check googletest
run: |
@@ -206,6 +206,72 @@ jobs:
[ ! -d "build/src/tests/googletest-build" ]
[ ! -d "build/src/tests/googletest-src" ]
+ cmake-system-sexpp:
+ name: system-sexpp, sexpp shared libs ${{ matrix.sexpp_shared_libs }}, rnp shared libs ${{ matrix.rnp_shared_libs }}
+ runs-on: ubuntu-latest
+ if: "!contains(github.event.head_commit.message, 'skip ci')"
+ timeout-minutes: 30
+ strategy:
+ fail-fast: false
+ matrix:
+ sexpp_shared_libs: [ 'on', 'off' ]
+ rnp_shared_libs: ['on', 'off']
+
+ steps:
+ - name: Install dependencies
+ run: |
+ sudo apt-get -y update
+ sudo apt-get -y install cmake libjson-c-dev libbotan-2-dev asciidoctor
+
+ - name: Checkout sexpp
+ uses: actions/checkout@v3
+ with:
+ repository: rnpgp/sexpp
+ path: sexpp
+
+ - name: Configure sexpp
+ run: |
+ echo CORES="$(nproc --all)" >> $GITHUB_ENV
+ cmake -S sexpp -B sexpp/build \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DDOWNLOAD_GTEST=OFF \
+ -DWITH_SEXP_TESTS=OFF \
+ -DBUILD_SHARED_LIBS=${{ matrix.sexpp_shared_libs}}
+
+ - name: Build sexpp
+ run: cmake --build sexpp/build --parallel ${{ env.CORES }}
+
+ - name: Install sexpp
+ run: sudo cmake --install sexpp/build
+
+ - name: Clean sexpp
+ run: rm -rf sexpp
+
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 1
+ submodules: false
+
+ - name: Configure
+ run: |
+ cmake -B build \
+ -DBUILD_SHARED_LIBS=${{ matrix.rnp_shared_libs }} \
+ -DCRYPTO_BACKEND=botan \
+ -DDOWNLOAD_GTEST=ON \
+ -DSYSTEM_LIBSEXPP=ON \
+ -DCMAKE_BUILD_TYPE=Release .
+
+ - name: Build
+ run: cmake --build build --parallel ${{ env.CORES }}
+
+ - name: Test
+ run: |
+ mkdir -p "build/Testing/Temporary"
+ cp "cmake/CTestCostData.txt" "build/Testing/Temporary"
+ export PATH="$PWD/build/src/lib:$PATH"
+ ctest --parallel ${{ env.CORES }} --test-dir build -R rnp_tests --output-on-failure
+
package-source:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'skip ci')"
diff --git a/.github/workflows/windows-native.yml b/.github/workflows/windows-native.yml
index 87c37ac..afed7e7 100644
--- a/.github/workflows/windows-native.yml
+++ b/.github/workflows/windows-native.yml
@@ -72,24 +72,14 @@ jobs:
arch: [ { name: 'x64', triplet: 'x64-windows' } ]
toolset: [ 'v142', 'ClangCL' ]
backend: [ 'botan', 'openssl' ]
- shared_libs: [ 'on', 'off']
- use_cmake_prefix_path: [ 'off' ]
+ shared_libs: [ 'off']
+ use_cmake_prefix_path: [ 'on', 'off' ]
include:
- - arch: { name: 'x64', triplet: 'x64-windows' }
- toolset: 'ClangCL'
- backend: 'openssl'
- use_cmake_prefix_path: 'on'
- shared_libs: 'on'
- - arch: { name: 'x64', triplet: 'x64-windows' }
- toolset: 'ClangCL'
- backend: 'openssl'
- use_cmake_prefix_path: 'on'
- shared_libs: 'off'
- arch: { name: 'Win32', triplet: 'x86-windows' }
toolset: 'ClangCL'
backend: 'botan'
- use_cmake_prefix_path: 'off'
- shared_libs: 'on'
+ use_cmake_prefix_path: 'on'
+ shared_libs: 'off'
- arch: { name: 'Win32', triplet: 'x86-windows' }
toolset: 'v142'
backend: 'openssl'
@@ -117,14 +107,21 @@ jobs:
- name: vcpkg packages
shell: bash
- run: vcpkg install --triplet ${{ matrix.arch.triplet }} bzip2 zlib json-c getopt dirent ${{ matrix.backend }}
+ run: |
+ dir_u=$(cygpath -u ${{ env.VCPKG_DIR }})
+ echo "VCPKG_DIR_U=$dir_u" >> $GITHUB_ENV
+ vcpkg install --triplet ${{ matrix.arch.triplet }} bzip2 zlib json-c getopt dirent ${{ matrix.backend }}
- name: Set OPENSSL_ROOT_DIR
# Ensure consistent access to openssl installation for test_backend_version test
# There is another one instance of ssl at /mingw and /mingw/bin is always at the first position at PATH
+ # So we have to adjust PATH for each step below; changing $GITHUB_PATH does not work
if: matrix.backend == 'openssl'
shell: bash
- run: echo OPENSSL_ROOT_DIR=${{ env.VCPKG_DIR }}/installed >> $GITHUB_ENV
+ run: |
+ echo OPENSSL_ROOT_DIR=${{ env.VCPKG_DIR_U }}/installed/${{ matrix.arch.triplet }} >> $GITHUB_ENV
+ echo OPENSSL_MODULES=${{ env.VCPKG_DIR_U }}/installed/${{ matrix.arch.triplet }}/bin >> $GITHUB_ENV
+ echo RNP_TESTS_OPENSSL_ROOT=${{ env.VCPKG_DIR_U }}/installed/${{ matrix.arch.triplet }} >> $GITHUB_ENV
- name: Adjust settings for s2k_iteration_tuning test
# This step adjusts s2k_iteration_tuning threshold for
@@ -139,10 +136,11 @@ jobs:
shell: bash
run: |
echo CORES="$(nproc --all)" >> $GITHUB_ENV
+ export PATH=${{ env.VCPKG_DIR_U }}/installed/${{ matrix.arch.triplet }}/bin:$PATH
cmake -B build -G "Visual Studio 16 2019" \
-A ${{ matrix.arch.name }} \
-T ${{ matrix.toolset }} \
- -DBUILD_SHARED_LIBS=${{ matrix.shared_lib}} \
+ -DBUILD_SHARED_LIBS=${{ matrix.shared_libs}} \
-DCRYPTO_BACKEND=${{ matrix.backend }} \
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_DIR }}/scripts/buildsystems/vcpkg.cmake .
@@ -151,24 +149,28 @@ jobs:
shell: bash
run: |
echo CORES="$(nproc --all)" >> $GITHUB_ENV
+ export PATH=${{ env.VCPKG_DIR_U }}/installed/${{ matrix.arch.triplet }}/bin:$PATH
cmake -B build -G "Visual Studio 16 2019" \
-A ${{ matrix.arch.name }} \
-T ${{ matrix.toolset }} \
- -DBUILD_SHARED_LIBS=${{ matrix.shared_lib}} \
+ -DBUILD_SHARED_LIBS=${{ matrix.shared_libs}} \
-DCRYPTO_BACKEND=${{ matrix.backend }} \
-DCMAKE_PREFIX_PATH=${{ env.VCPKG_DIR }}/installed/${{ matrix.arch.triplet }} .
- echo ${{ env.VCPKG_DIR }}/installed/${{ matrix.arch.triplet }}/bin >> $GITHUB_PATH
- - name: Compile
+ - name: Build
shell: bash
- run: cmake --build build --config "Release" --parallel ${{ env.CORES }}
+ run: |
+ export PATH=${{ env.VCPKG_DIR_U }}/installed/${{ matrix.arch.triplet }}/bin:$PATH
+ cmake --build build --config "Release" --parallel ${{ env.CORES }}
- name: Test
shell: bash
# Sometimes running cli_tests in parallel causes instability [???]
# ctest --test-dir build -R cli_tests -C Debug --output-on-failure
# ctest --parallel ${{ env.CORES }} --test-dir build -R rnp_tests -C Debug --output-on-failure
+ # ctest --parallel ${{ env.CORES }} --test-dir build -C Release --output-on-failure
run: |
+ export PATH=${{ env.VCPKG_DIR_U }}/installed/${{ matrix.arch.triplet }}/bin:$PATH
mkdir -p "build/Testing/Temporary"
cp "cmake/CTestCostData.txt" "build/Testing/Temporary"
- ctest --parallel ${{ env.CORES }} --test-dir build -C Debug --output-on-failure
+ ctest --parallel ${{ env.CORES }} --test-dir build -C Release --output-on-failure
diff --git a/.gitignore b/.gitignore
index 773a9bd..cc6cdc5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -461,4 +461,7 @@ $RECYCLE.BIN/
# Windows shortcuts
*.lnk
+# Vim and its plugins
+.ycm_extra_conf.py
+
# End of https://www.toptal.com/developers/gitignore/api/c,vim,c++,macos,linux,patch,cmake,emacs,vscode,python,windows,textmate,sublimetext
diff --git a/.gitmodules b/.gitmodules
index 6cdbd78..3fa1441 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
-[submodule "src/libsexp"]
- path = src/libsexp
- url = https://github.com/rnpgp/sexp.git
+[submodule "src/libsexpp"]
+ path = src/libsexpp
+ url = https://github.com/rnpgp/sexpp.git
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 611e461..e17f8e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
## Changelog
+### 0.17.1 [2024-04-08]
+
+#### General
+
+* Added support for Botan 3.
+* Updated support for OpenSSL 3.
+* Added support for mimemode in literal data packet.
+* Relaxed Base64 decoding to allow spaces after the checksum.
+
+#### FFI
+
+* Added functions `rnp_key_set_features()` and `rnp_signature_get_features()`.
+
### 0.17.0 [2023-05-01]
#### General
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bb6d40c..ecf2191 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2018-2020 Ribose Inc.
+# Copyright (c) 2018-2023 Ribose Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -58,6 +58,8 @@ option(ENABLE_COVERAGE "Enable code coverage testing.")
option(ENABLE_SANITIZERS "Enable ASan and other sanitizers.")
option(ENABLE_FUZZERS "Enable fuzz targets.")
option(DOWNLOAD_GTEST "Download Googletest" On)
+option(SYSTEM_LIBSEXPP "Use system sexpp library" OFF)
+
# crypto components
function(tristate_feature_auto NAME DESCRIPTION)
set(${NAME} Auto CACHE STRING ${DESCRIPTION})
@@ -98,14 +100,20 @@ endif()
# crypto backend
if (NOT CRYPTO_BACKEND)
set(CRYPTO_BACKEND "botan" CACHE STRING
- "Crypto backend. Possible values are botan and openssl (under development, only for debug builds). Default is botan."
+ "Crypto backend. Possible values are botan and openssl. Default is botan."
FORCE
)
endif()
string(TOLOWER ${CRYPTO_BACKEND} CRYPTO_BACKEND_LOWERCASE)
if(CRYPTO_BACKEND_LOWERCASE STREQUAL "botan")
- # Default value;
+ # Default value; version 2 or 3 of Botan
+ set(CRYPTO_BACKEND_BOTAN 1)
+elseif(CRYPTO_BACKEND_LOWERCASE STREQUAL "botan3")
+ set(CRYPTO_BACKEND "botan")
+ set(CRYPTO_BACKEND_LOWERCASE "botan")
+ # Require version 3 of Botan
set(CRYPTO_BACKEND_BOTAN 1)
+ set(CRYPTO_BACKEND_BOTAN3 1)
elseif(CRYPTO_BACKEND_LOWERCASE STREQUAL "openssl")
set(CRYPTO_BACKEND_OPENSSL 1)
else()
@@ -128,7 +136,7 @@ if(NOT MSVC)
)
endif(NOT MSVC)
-# THis works both for MSVC and CL on Windows
+# This works both for MSVC and CL on Windows
if(WIN32)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
endif(WIN32)
@@ -176,10 +184,36 @@ if (ENABLE_FUZZERS)
endif()
add_subdirectory(src/common)
-set(WITH_SEXP_CLI OFF)
-set(WITH_SEXP_TESTS OFF)
-set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME development)
-add_subdirectory(src/libsexp EXCLUDE_FROM_ALL)
+if (SYSTEM_LIBSEXPP)
+ find_package(PkgConfig QUIET)
+ pkg_check_modules(SEXPP sexpp>=0.8.7 REQUIRED)
+ find_library(SEXPP_LIBRARY
+ NAMES
+ "libsexpp"
+ "sexpp"
+ HINTS
+ "${SEXPP_LIBRARY_DIRS}"
+ )
+ add_library(sexpp UNKNOWN IMPORTED)
+ set_target_properties(sexpp
+ PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${SEXPP_INCLUDE_DIR}"
+ IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
+ IMPORTED_LOCATION "${SEXPP_LIBRARY}"
+ )
+else (SYSTEM_LIBSEXPP)
+# If we use system libsexpp is not used we build sexpp static library
+# If librnp is shared, libsexpp.a is a transient artifact which is hidden from
+# the end user.
+# If librnp is static we install libsexpp.a aside
+ set(SAVED_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS})
+ set(BUILD_SHARED_LIBS OFF)
+ set(WITH_SEXP_CLI OFF)
+ set(WITH_SEXP_TESTS OFF)
+ set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME development)
+ add_subdirectory(src/libsexpp EXCLUDE_FROM_ALL)
+ set(BUILD_SHARED_LIBS ${SAVED_BUILD_SHARED_LIBS})
+endif (SYSTEM_LIBSEXPP)
add_subdirectory(src/lib)
add_subdirectory(src/rnp)
diff --git a/README.adoc b/README.adoc
index a0162fa..bcfe062 100644
--- a/README.adoc
+++ b/README.adoc
@@ -28,6 +28,7 @@ Currently supported platforms:
* RHEL/CentOS
* Ubuntu
* NixOS / Nix
+* Gentoo
* FreeBSD
* MacOS
* Windows
diff --git a/ci/build_package_rpm.sh b/ci-legacy/build_package_rpm.sh
index 645bf5d..645bf5d 100755
--- a/ci/build_package_rpm.sh
+++ b/ci-legacy/build_package_rpm.sh
diff --git a/ci/env-common.inc.sh b/ci-legacy/env-common.inc.sh
index 11712f0..1e2f9ed 100644
--- a/ci/env-common.inc.sh
+++ b/ci-legacy/env-common.inc.sh
@@ -21,9 +21,5 @@ if [ "$BUILD_MODE" = "sanitize" ]; then
export CC=clang
fi
-BOTAN_MODULES=$(<ci/botan-modules tr '\n' ',')
-
-export BOTAN_MODULES
-
# Don't clean up tempdirs when in CI runners to save time. Unset to disable.
export RNP_KEEP_TEMP=1
diff --git a/ci/env-freebsd.inc.sh b/ci-legacy/env-freebsd.inc.sh
index 3509111..3509111 100644
--- a/ci/env-freebsd.inc.sh
+++ b/ci-legacy/env-freebsd.inc.sh
diff --git a/ci/env-linux.inc.sh b/ci-legacy/env-linux.inc.sh
index 9267972..9267972 100644
--- a/ci/env-linux.inc.sh
+++ b/ci-legacy/env-linux.inc.sh
diff --git a/ci/env.inc.sh b/ci-legacy/env.inc.sh
index ab2271f..ab2271f 100644
--- a/ci/env.inc.sh
+++ b/ci-legacy/env.inc.sh
diff --git a/ci/gha/setup-env.inc.sh b/ci-legacy/gha/setup-env.inc.sh
index c0aca85..c0aca85 100644
--- a/ci/gha/setup-env.inc.sh
+++ b/ci-legacy/gha/setup-env.inc.sh
diff --git a/ci/install.sh b/ci-legacy/install.sh
index 9d90265..9d90265 100755
--- a/ci/install.sh
+++ b/ci-legacy/install.sh
diff --git a/ci/install_cacheable_dependencies.sh b/ci-legacy/install_cacheable_dependencies.sh
index ea23ad6..ea23ad6 100755
--- a/ci/install_cacheable_dependencies.sh
+++ b/ci-legacy/install_cacheable_dependencies.sh
diff --git a/ci/install_noncacheable_dependencies.sh b/ci-legacy/install_noncacheable_dependencies.sh
index bfb93da..bfb93da 100755
--- a/ci/install_noncacheable_dependencies.sh
+++ b/ci-legacy/install_noncacheable_dependencies.sh
diff --git a/ci/lib/cacheable_install_functions.inc.sh b/ci-legacy/lib/cacheable_install_functions.inc.sh
index 6ef35bb..b58d8d9 100644
--- a/ci/lib/cacheable_install_functions.inc.sh
+++ b/ci-legacy/lib/cacheable_install_functions.inc.sh
@@ -14,20 +14,26 @@
install_botan() {
# botan
local botan_build=${LOCAL_BUILDS}/botan
- if [[ ! -e "${BOTAN_INSTALL}/lib/libbotan-2.so" ]] && \
- [[ ! -e "${BOTAN_INSTALL}/lib/libbotan-2.dylib" ]] && \
- [[ ! -e "${BOTAN_INSTALL}/lib/libbotan-2.a" ]]; then
+ local botan_v=${BOTAN_VERSION::1}
+ if [[ ! -e "${BOTAN_INSTALL}/lib/libbotan-${botan_v}.so" ]] && \
+ [[ ! -e "${BOTAN_INSTALL}/lib/libbotan-${botan_v}.dylib" ]] && \
+ [[ ! -e "${BOTAN_INSTALL}/lib/libbotan-${botan_v}.a" ]]; then
if [[ -d "${botan_build}" ]]; then
rm -rf "${botan_build}"
fi
git clone --depth 1 --branch "${BOTAN_VERSION}" https://github.com/randombit/botan "${botan_build}"
- pushd "${botan_build}"
local osparam=()
local cpuparam=()
local run=run
+ local osslparam=()
+ local modules=""
+ [[ "${botan_v}" == "2" ]] && osslparam+=("--without-openssl") && modules=$(<ci/botan-modules tr '\n' ',')
+ [[ "${botan_v}" == "3" ]] && modules=$(<ci/botan3-modules tr '\n' ',')
+
+ pushd "${botan_build}"
# Position independent code is a default for shared libraries at any xNIX platform
# but it makes no sense and is not supported for Windows
local extra_cflags="-fPIC"
@@ -47,8 +53,8 @@ install_botan() {
is_use_static_dependencies && build_target="static,cli"
"${run}" ./configure.py --prefix="${BOTAN_INSTALL}" --with-debug-info --extra-cxxflags="-fno-omit-frame-pointer ${extra_cflags}" \
- ${osparam+"${osparam[@]}"} ${cpuparam+"${cpuparam[@]}"} --without-documentation --without-openssl --build-targets="${build_target}" \
- --minimized-build --enable-modules="$BOTAN_MODULES"
+ ${osparam+"${osparam[@]}"} ${cpuparam+"${cpuparam[@]}"} --without-documentation ${osslparam+"${osslparam[@]}"} --build-targets="${build_target}" \
+ --minimized-build --enable-modules="$modules"
${MAKE} -j"${MAKE_PARALLEL}" install
popd
fi
@@ -224,11 +230,11 @@ install_gpg() {
# npth libgpg-error libgcrypt libassuan libksba pinentry gnupg
_install_gpg component-version 1.6 1.46 1.8.10 2.5.5 1.6.3 1.2.1 2.2.41
;;
- beta)
+ # beta)
# npth libgpg-error libgcrypt libassuan libksba pinentry gnupg
- _install_gpg component-git-ref 2501a48 f73605e d9c4183 909133b 3df0cd3 0e2e53c c6702d7
+ # _install_gpg component-git-ref 2501a48 f73605e d9c4183 909133b 3df0cd3 0e2e53c c6702d7
# _install_gpg component-git-ref 7e45b50 c66594d cf88dca 57cf9d6 4243085 6e8ad31 d4e5979
- ;;
+ # ;;
"2.3.1")
# npth libgpg-error libgcrypt libassuan libksba pinentry gnupg
_install_gpg component-version 1.6 1.42 1.9.3 2.5.5 1.6.0 1.1.1 2.3.1
diff --git a/ci/lib/install_functions.inc.sh b/ci-legacy/lib/install_functions.inc.sh
index 98acc82..98acc82 100644
--- a/ci/lib/install_functions.inc.sh
+++ b/ci-legacy/lib/install_functions.inc.sh
diff --git a/ci/local.sh b/ci-legacy/local.sh
index db687ff..db687ff 100755
--- a/ci/local.sh
+++ b/ci-legacy/local.sh
diff --git a/ci/main.sh b/ci-legacy/main.sh
index c8d80c9..c8d80c9 100755
--- a/ci/main.sh
+++ b/ci-legacy/main.sh
diff --git a/ci/run.sh b/ci-legacy/run.sh
index b9e366c..b9e366c 100755
--- a/ci/run.sh
+++ b/ci-legacy/run.sh
diff --git a/ci/style-check.sh b/ci-legacy/style-check.sh
index 683bbcd..683bbcd 100755
--- a/ci/style-check.sh
+++ b/ci-legacy/style-check.sh
diff --git a/ci/success.sh b/ci-legacy/success.sh
index 1a34be7..1a34be7 100755
--- a/ci/success.sh
+++ b/ci-legacy/success.sh
diff --git a/ci/utils.inc.sh b/ci-legacy/utils.inc.sh
index 618df78..618df78 100644
--- a/ci/utils.inc.sh
+++ b/ci-legacy/utils.inc.sh
diff --git a/ci/botan3-modules b/ci/botan3-modules
new file mode 100644
index 0000000..40f5a26
--- /dev/null
+++ b/ci/botan3-modules
@@ -0,0 +1,46 @@
+aead
+aes
+auto_rng
+bigint
+blowfish
+camellia
+cast128
+cbc
+cfb
+crc24
+curve25519
+des
+dl_algo
+dl_group
+dsa
+eax
+ecc_key
+ecdh
+ecdsa
+ed25519
+elgamal
+eme_pkcs1
+emsa_pkcs1
+emsa_raw
+ffi
+hash
+raw_hash
+hmac
+hmac_drbg
+idea
+kdf
+md5
+ocb
+pgp_s2k
+rfc3394
+rmd160
+rsa
+sha1
+sha2_32
+sha2_64
+sha3
+sm2
+sm3
+sm4
+sp800_56a
+twofish
diff --git a/ci/docker/Makefile b/ci/docker/Makefile
deleted file mode 100644
index b5e9877..0000000
--- a/ci/docker/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-all: fedora35.pushed
-clean:
- rm *.built *.pushed
-.PHONY: all clean
-
-fedora35.built:
- cd ../.. && docker build --squash -t andreyutkin/rnp-ci-fedora:35 -f ci/docker/fedora35.Dockerfile .
- touch $@
-fedora35.pushed: fedora35.built
- docker push andreyutkin/rnp-ci-fedora:35
- touch $@
diff --git a/ci/docker/fedora35.Dockerfile b/ci/docker/fedora35.Dockerfile
deleted file mode 100644
index dd71b05..0000000
--- a/ci/docker/fedora35.Dockerfile
+++ /dev/null
@@ -1,52 +0,0 @@
-FROM fedora:35
-RUN dnf makecache
-RUN dnf -y install \
-autoconf \
-automake \
-bison \
-botan2 \
-botan2-devel \
-byacc \
-bzip2 \
-bzip2-devel \
-clang \
-cmake \
-gcc \
-gcc-c++ \
-gettext-devel \
-git \
-gtest \
-gtest-devel \
-gzip \
-json-c \
-json-c-devel \
-libtool \
-make \
-ncurses-devel \
-openssl \
-openssl-devel \
-openssl-libs \
-python3 \
-ruby-devel \
-rubygem-asciidoctor \
-sudo \
-wget \
-zlib-devel \
-;
-RUN dnf clean all
-
-RUN useradd rnpuser
-RUN echo -e "rnpuser\tALL=(ALL)\tNOPASSWD:\tALL" > /etc/sudoers.d/rnpuser
-RUN echo -e "rnpuser\tsoft\tnproc\tunlimited\n" > /etc/security/limits.d/30-rnpuser.conf
-
-# Everything below wouldn't be needed if packaged gpg didn't fail with "Unknown elliptic curve"
-# on these tests from cli_tests.Misc:
-# test_aead_last_chunk_zero_length
-# test_clearsign_long_lines
-# test_eddsa_sig_lead_zero
-# test_text_sig_crcr
-
-COPY ci ci
-RUN export USE_STATIC_DEPENDENCIES=yes && su rnpuser -c ci/install_noncacheable_dependencies.sh
-RUN export USE_STATIC_DEPENDENCIES=yes && su rnpuser -c ci/install_cacheable_dependencies.sh
-RUN rm -rf /home/rnpuser/local-builds
diff --git a/ci/docker/fedora35.test-locally b/ci/docker/fedora35.test-locally
deleted file mode 100755
index 892c9a6..0000000
--- a/ci/docker/fedora35.test-locally
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-
-rm -rf /tmp/rnp*
-
-cat > run-this <<EOF
-#!/bin/bash
-set -e
-set -x
-
-export USE_STATIC_DEPENDENCIES=no
-
-export BUILD_MODE=normal CC=gcc CXX=g++ CRYPTO_BACKEND=openssl
-#export BUILD_MODE=sanitize CC=clang CXX=clang++ CRYPTO_BACKEND=botan
-
-cp -a /rnp /rnp.container
-cd /rnp.container
-chown -R rnpuser:rnpuser .
-su rnpuser -c ci/run.sh || bash -i
-EOF
-chmod a+x run-this
-
-docker run -v $PWD:/rnp -it andreyutkin/rnp-ci-fedora:35 /rnp/run-this
diff --git a/ci/tests/ci-tests.sh b/ci/tests/ci-tests.sh
index 1aaba03..73c724d 100755
--- a/ci/tests/ci-tests.sh
+++ b/ci/tests/ci-tests.sh
@@ -106,7 +106,7 @@ test_supported_features() {
;;
*)
so_folder="lib64"
- botan_only+=("${brainpool[@]}")
+# botan_only+=("${brainpool[@]}")
esac
if [[ "${CRYPTO_BACKEND:-}" == "openssl" ]]; then
@@ -118,9 +118,9 @@ test_supported_features() {
fi
if [[ "$OSTYPE" == darwin* ]]; then
- export DYLD_LIBRARY_PATH="$library_path"
+ export DYLD_LIBRARY_PATH="$library_path${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}}"
else
- export LD_LIBRARY_PATH="$library_path"
+ export LD_LIBRARY_PATH="$library_path${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
fi
"$RNP_INSTALL"/bin/rnp --version > rnp-version
diff --git a/ci/tests/deb-tests.sh b/ci/tests/deb-tests.sh
index 7805702..3563022 100755
--- a/ci/tests/deb-tests.sh
+++ b/ci/tests/deb-tests.sh
@@ -43,8 +43,6 @@ declare expected_libraries=(
declare expected_devlibraries=(
"$DIR_LIB/librnp.so"
- "$DIR_LIB/librnp.a"
- "$DIR_LIB/libsexp.a"
"$DIR_LIB/pkgconfig/librnp.pc"
)
diff --git a/ci/tests/pk-tests.sh b/ci/tests/pk-tests.sh
index 2b22df9..cd496b4 100755
--- a/ci/tests/pk-tests.sh
+++ b/ci/tests/pk-tests.sh
@@ -63,8 +63,8 @@ create_cmake_file() {
set_target_properties(JSON-C::JSON-C PROPERTIES INTERFACE_LINK_LIBRARIES PkgConfig::JSONC)
pkg_check_modules(Botan REQUIRED IMPORTED_TARGET botan-2)
- add_library(Botan2::Botan2 INTERFACE IMPORTED)
- set_target_properties(Botan2::Botan2 PROPERTIES INTERFACE_LINK_LIBRARIES PkgConfig::Botan)
+ add_library(Botan::Botan INTERFACE IMPORTED)
+ set_target_properties(Botan::Botan PROPERTIES INTERFACE_LINK_LIBRARIES PkgConfig::Botan)
find_package(rnp REQUIRED)
@@ -101,33 +101,6 @@ test_shared_library() {
sudo yum -y erase $(rpm -qa | grep rnp)
}
-test_static_library() {
- sudo yum -y localinstall librnp0-0*.*.rpm librnp0-devel-0*.*.rpm
- pushd "$(mktemp -d)"
- create_source_file
- create_cmake_file 'rnp::librnp-static'
-
-# shellcheck disable=SC2251
-! cmake . -DCMAKE_MODULE_PATH="$DIR_CMAKE"/*
- assertEquals "cmake failed at static library test" 0 "${PIPESTATUS[0]}"
-
-# shellcheck disable=SC2251
-! make
- assertEquals "make failed at static library test" 0 "${PIPESTATUS[0]}"
-
-# shellcheck disable=SC2251
-! ./find_package_test
- assertEquals "test program failed at static library test" 0 "${PIPESTATUS[0]}"
-
-# shellcheck disable=SC2251
-! ldd find_package_test | grep librnp
- assertNotEquals "unexpected reference to shared rnp library at static library test" 0 "${PIPESTATUS[1]}"
-
- popd
-# shellcheck disable=SC2046
- sudo yum -y erase $(rpm -qa | grep rnp)
-}
-
test_no_library() {
pushd "$(mktemp -d)"
create_source_file
diff --git a/ci/tests/pkg-tests.sh b/ci/tests/pkg-tests.sh
index dbeaac6..dcc4d01 100755
--- a/ci/tests/pkg-tests.sh
+++ b/ci/tests/pkg-tests.sh
@@ -43,8 +43,6 @@ declare expected_libraries=(
declare expected_devlibraries=(
"$DIR_LIB/librnp.so"
- "$DIR_LIB/librnp.a"
- "$DIR_LIB/libsexp.a"
"$DIR_LIB/pkgconfig/librnp.pc"
)
diff --git a/ci/tests/rpm-tests.sh b/ci/tests/rpm-tests.sh
index 39f8dd0..75b676e 100755
--- a/ci/tests/rpm-tests.sh
+++ b/ci/tests/rpm-tests.sh
@@ -50,8 +50,6 @@ declare expected_libraries=(
declare expected_devlibraries=(
"$DIR_LIB/librnp.so"
- "$DIR_LIB/librnp.a"
- "$DIR_LIB/libsexp.a"
"$DIR_LIB/pkgconfig/librnp.pc"
)
diff --git a/cmake/CTestCostData.txt b/cmake/CTestCostData.txt
index 1a00177..03f3c61 100644
--- a/cmake/CTestCostData.txt
+++ b/cmake/CTestCostData.txt
@@ -1,159 +1,274 @@
-rnp_tests.hash_test_success 1 0.0138223
-rnp_tests.cipher_test_success 1 0.0120679
-rnp_tests.pkcs1_rsa_test_success 1 0.0714414
-rnp_tests.rnp_test_eddsa 1 0.014394
-rnp_tests.rnp_test_x25519 1 0.0197451
-rnp_tests.raw_elgamal_random_key_test_success 1 0.725944
-rnp_tests.ecdsa_signverify_success 1 0.0561304
-rnp_tests.ecdh_roundtrip 1 0.0383161
-rnp_tests.ecdh_decryptionNegativeCases 1 0.0201392
-rnp_tests.sm2_roundtrip 1 0.0214206
-rnp_tests.sm2_sm3_signature_test 1 0.027973
-rnp_tests.sm2_sha256_signature_test 1 0.0269801
-rnp_tests.test_dsa_roundtrip 1 24.2023
-rnp_tests.test_dsa_verify_negative 1 0.800061
-rnp_tests.s2k_iteration_tuning 1 0.515675
-rnp_tests.s2k_iteration_encode_decode 1 0.0134686
-rnp_tests.test_validate_key_material 1 1.68337
-rnp_tests.test_cli_rnp_keyfile 1 0.859851
-rnp_tests.test_cli_g10_operations 1 2.50301
-rnp_tests.test_cli_rnp 1 0.0972449
-rnp_tests.test_cli_examples 1 1.3149
-rnp_tests.test_cli_rnpkeys 1 0.115061
-rnp_tests.test_cli_dump 1 0.0361111
-rnp_tests.test_cli_logname 1 0.0116097
-rnp_tests.rnpkeys_exportkey_verifyUserId 1 0.118051
-rnp_tests.test_ffi_homedir 1 0.0241839
-rnp_tests.test_ffi_detect_key_format 1 0.0129409
-rnp_tests.test_ffi_load_keys 1 0.0493167
-rnp_tests.test_ffi_clear_keys 1 0.0259738
-rnp_tests.test_ffi_save_keys 1 0.0316757
-rnp_tests.test_ffi_keygen_json_pair 1 0.0736696
-rnp_tests.test_ffi_keygen_json_pair_dsa_elg 1 12.7012
-rnp_tests.test_ffi_keygen_json_primary 1 0.0194215
-rnp_tests.test_ffi_keygen_json_sub 1 0.0573184
-rnp_tests.test_ffi_key_generate_misc 1 0.985612
-rnp_tests.test_ffi_key_generate_rsa 1 0.342821
-rnp_tests.test_ffi_key_generate_dsa 1 2.13331
-rnp_tests.test_ffi_key_generate_ecdsa 1 0.0177323
-rnp_tests.test_ffi_key_generate_eddsa 1 0.0143182
-rnp_tests.test_ffi_key_generate_sm2 1 0.0175289
-rnp_tests.test_ffi_key_generate_ex 1 5.74498
-rnp_tests.test_ffi_key_generate_algnamecase 1 2.62221
-rnp_tests.test_ffi_key_generate_protection 1 1.40079
-rnp_tests.test_ffi_add_userid 1 0.0404918
-rnp_tests.test_ffi_keygen_json_sub_pass_required 1 0.382914
-rnp_tests.test_ffi_encrypt_pass 1 0.429726
-rnp_tests.test_ffi_encrypt_pass_provider 1 0.342456
-rnp_tests.test_ffi_encrypt_pk 1 0.0482677
-rnp_tests.test_ffi_encrypt_pk_key_provider 1 0.0339246
-rnp_tests.test_ffi_encrypt_and_sign 1 0.0917957
-rnp_tests.test_ffi_signatures_memory 1 0.0518454
-rnp_tests.test_ffi_signatures 1 0.0488163
-rnp_tests.test_ffi_signatures_detached_memory 1 0.0492941
-rnp_tests.test_ffi_signatures_detached 1 0.04846
-rnp_tests.test_ffi_signatures_dump 1 0.0168647
-rnp_tests.test_ffi_key_to_json 1 0.0164411
-rnp_tests.test_ffi_key_iter 1 0.0261607
-rnp_tests.test_ffi_locate_key 1 0.0161026
-rnp_tests.test_ffi_signatures_detached_memory_g10 1 0.0427063
-rnp_tests.test_ffi_enarmor_dearmor 1 0.0373763
-rnp_tests.test_ffi_version 1 0.0140884
-rnp_tests.test_ffi_key_export 1 0.0250205
-rnp_tests.test_ffi_key_dump 1 0.0256317
-rnp_tests.test_ffi_pkt_dump 1 0.0167862
-rnp_tests.test_ffi_rsa_v3_dump 1 0.0158175
-rnp_tests.test_ffi_load_userattr 1 0.0177854
-rnp_tests.test_ffi_revocations 1 0.0191412
-rnp_tests.test_ffi_file_output 1 0.0292883
-rnp_tests.test_ffi_key_signatures 1 0.0211163
-rnp_tests.test_ffi_keys_import 1 0.577245
-rnp_tests.test_ffi_import_keys_check_pktlen 1 0.0151679
-rnp_tests.test_ffi_calculate_iterations 1 0.0279029
-rnp_tests.test_ffi_supported_features 1 0.0149266
-rnp_tests.test_ffi_enable_debug 1 0.010948
-rnp_tests.test_ffi_rnp_key_get_primary_grip 1 0.0178161
-rnp_tests.test_ffi_output_to_armor 1 0.01893
-rnp_tests.test_ffi_rnp_guess_contents 1 0.0121287
-rnp_tests.test_ffi_literal_filename 1 0.0552985
-rnp_tests.test_ffi_op_set_hash 1 0.0528855
-rnp_tests.test_ffi_op_set_compression 1 0.0730627
-rnp_tests.test_ffi_aead_params 1 0.241513
-rnp_tests.test_ffi_detached_verify_input 1 0.0228228
-rnp_tests.test_ffi_op_verify_sig_count 1 0.0491328
-rnp_tests.rnpkeys_generatekey_testSignature 1 1.18378
-rnp_tests.rnpkeys_generatekey_testEncryption 1 0.275621
-rnp_tests.rnpkeys_generatekey_verifySupportedHashAlg 1 1.41153
-rnp_tests.rnpkeys_generatekey_verifyUserIdOption 1 0.616167
-rnp_tests.rnpkeys_generatekey_verifykeyHomeDirOption 1 0.188337
-rnp_tests.rnpkeys_generatekey_verifykeyKBXHomeDirOption 1 0.205113
-rnp_tests.rnpkeys_generatekey_verifykeyHomeDirNoPermission 1 0.149937
-rnp_tests.rnpkeys_generatekey_testExpertMode 1 2.63892
-rnp_tests.generatekeyECDSA_explicitlySetSmallOutputDigest_DigestAlgAdjusted 1 0.0200285
-rnp_tests.generatekey_multipleUserIds_ShouldFail 1 0.0110169
-rnp_tests.generatekeyECDSA_explicitlySetBiggerThanNeededDigest_ShouldSuceed 1 0.0187109
-rnp_tests.generatekeyECDSA_explicitlySetUnknownDigest_ShouldFail 1 0.0133541
-rnp_tests.test_generated_key_sigs 1 0.128008
-rnp_tests.test_kbx_nsigs 1 4.07201
-rnp_tests.test_key_add_userid 1 0.0324633
-rnp_tests.key_grip 1 0.0621873
-rnp_tests.test_key_prefs 1 0.0115654
-rnp_tests.test_key_protect_load_pgp 1 0.330786
-rnp_tests.test_key_store_search 1 0.0108802
-rnp_tests.test_key_store_search_by_name 1 0.0138023
-rnp_tests.test_key_unlock_pgp 1 0.0629279
-rnp_tests.test_key_validate 1 0.0313496
-rnp_tests.test_forged_key_validate 1 0.0350325
-rnp_tests.test_key_validity 1 0.0179677
-rnp_tests.test_large_packet 1 12.6562
-rnp_tests.test_load_g10 1 0.493759
-rnp_tests.test_load_v3_keyring_pgp 1 0.012287
-rnp_tests.test_load_v4_keyring_pgp 1 0.015202
-rnp_tests.test_load_keyring_and_count_pgp 1 0.0166739
-rnp_tests.test_load_check_bitfields_and_times 1 0.0145676
-rnp_tests.test_load_check_bitfields_and_times_v3 1 0.0141121
-rnp_tests.test_load_armored_pub_sec 1 0.0233942
-rnp_tests.test_load_merge 1 0.148499
-rnp_tests.test_load_public_from_secret 1 0.0321099
-rnp_tests.test_key_import 1 0.256372
-rnp_tests.test_load_subkey 1 0.0191733
-rnp_tests.test_partial_length_public_key 1 0.0120663
-rnp_tests.test_partial_length_signature 1 0.0141925
-rnp_tests.test_partial_length_first_packet_256 1 0.017635
-rnp_tests.test_partial_length_zero_last_chunk 1 0.0177394
-rnp_tests.test_partial_length_largest 1 3.20183
-rnp_tests.test_partial_length_first_packet_length 1 0.0300068
-rnp_tests.test_s2k_iterations 1 7.229982
-rnp_tests.test_stream_memory 1 0.0125394
-rnp_tests.test_stream_memory_discard 1 0.0111196
-rnp_tests.test_stream_file 1 0.0168583
-rnp_tests.test_stream_signatures 1 0.0373302
-rnp_tests.test_stream_signatures_revoked_key 1 0.012537
-rnp_tests.test_stream_key_load 1 0.0130797
-rnp_tests.test_stream_key_load_errors 1 0.727713
-rnp_tests.test_stream_key_decrypt 1 0.511516
-rnp_tests.test_stream_key_encrypt 1 0.487256
-rnp_tests.test_stream_key_signatures 1 0.0199383
-rnp_tests.test_stream_key_signature_validate 1 0.108001
-rnp_tests.test_stream_verify_no_key 1 0.0301321
-rnp_tests.test_stream_dumper 1 0.0180775
-rnp_tests.test_stream_z 1 21.1022
-rnp_tests.test_stream_814_dearmor_double_free 1 0.0119918
-rnp_tests.test_stream_825_dearmor_blank_line 1 0.0155773
-rnp_tests.test_stream_dearmor_edge_cases 1 0.0119838
-rnp_tests.test_load_user_prefs 1 0.0142821
-rnp_tests.test_utils_list 1 0.0107737
-rnp_tests.test_rnpcfg 1 0.0108745
-rnp_tests.issue_1030_rnpkeys_secret_keys_unprotected 1 0.31969
-setupTestData 1 0.0175373
-cli_tests-Keystore 1 5.56182
-cli_tests-SignECDSA 1 3.92326
-cli_tests-Compression 1 137.827
-cli_tests-Encryption 1 329.997
-cli_tests-Misc 1 35.6212
-cli_tests-SignDefault 1 16.0034
-cli_tests-EncryptEcdh 1 4.19268
-cli_tests-SignDSA 1 6.13993
-cli_tests-EncryptSignRSA 1 2.47967
-cli_tests-EncryptElgamal 1 11.2535
+rnp_tests.hash_test_success 1 0.0523389
+rnp_tests.cipher_test_success 1 0.0527984
+rnp_tests.pkcs1_rsa_test_success 1 0.115839
+rnp_tests.rnp_test_eddsa 1 0.0525285
+rnp_tests.rnp_test_x25519 1 0.0669975
+rnp_tests.raw_elgamal_random_key_test_success 1 10.9527
+rnp_tests.ecdsa_signverify_success 1 0.0972991
+rnp_tests.ecdh_roundtrip 1 0.0830944
+rnp_tests.ecdh_decryptionNegativeCases 1 0.0617576
+rnp_tests.sm2_roundtrip 1 0.0636458
+rnp_tests.sm2_sm3_signature_test 1 0.060542
+rnp_tests.sm2_sha256_signature_test 1 0.0602844
+rnp_tests.test_dsa_roundtrip 1 22.338
+rnp_tests.test_dsa_verify_negative 1 0.68138
+rnp_tests.s2k_iteration_tuning 1 8.38086
+rnp_tests.s2k_iteration_encode_decode 1 0.0494878
+rnp_tests.test_validate_key_material 1 2.77391
+rnp_tests.test_sm2_enabled 1 0.0514602
+rnp_tests.test_aead_enabled 1 0.0479014
+rnp_tests.test_idea_enabled 1 0.0501038
+rnp_tests.test_twofish_enabled 1 0.0480356
+rnp_tests.test_brainpool_enabled 1 0.0489269
+rnp_tests.test_windows_botan_crash 1 0.0503856
+rnp_tests.test_cipher_idea 1 0.0676931
+rnp_tests.test_cipher_aes_128_ocb 1 0.0573161
+rnp_tests.test_cipher_aes_128_cbc 1 0.0494284
+rnp_tests.test_cipher_aes_128_cbc_nopadding 1 0.058429
+rnp_tests.test_cli_rnp_keyfile 1 0.637305
+rnp_tests.test_cli_g10_operations 1 4.91057
+rnp_tests.test_cli_rnpkeys_unicode 1 0.054029
+rnp_tests.test_cli_rnp 1 0.177541
+rnp_tests.test_cli_examples 1 1.91233
+rnp_tests.test_cli_rnpkeys 1 0.183342
+rnp_tests.test_cli_rnpkeys_genkey 1 1.87305
+rnp_tests.test_cli_dump 1 0.0697788
+rnp_tests.test_cli_logname 1 0.0488487
+rnp_tests.rnpkeys_exportkey_verifyUserId 1 0.154042
+rnp_tests.test_ffi_homedir 1 0.0590592
+rnp_tests.test_ffi_detect_key_format 1 0.0560153
+rnp_tests.test_ffi_load_keys 1 0.101535
+rnp_tests.test_ffi_clear_keys 1 0.0658103
+rnp_tests.test_ffi_save_keys 1 0.0769988
+rnp_tests.test_ffi_load_save_keys_to_utf8_path 1 0.0685385
+rnp_tests.test_ffi_add_userid 1 0.100773
+rnp_tests.test_ffi_signatures_memory 1 0.0876108
+rnp_tests.test_ffi_signatures 1 0.0895412
+rnp_tests.test_ffi_signatures_detached_memory 1 0.0875415
+rnp_tests.test_ffi_signatures_detached 1 0.0874103
+rnp_tests.test_ffi_signatures_dump 1 0.0553215
+rnp_tests.test_ffi_locate_key 1 0.0553068
+rnp_tests.test_ffi_signatures_detached_memory_g10 1 0.0877412
+rnp_tests.test_ffi_enarmor_dearmor 1 0.0634234
+rnp_tests.test_ffi_dearmor_edge_cases 1 0.0507168
+rnp_tests.test_ffi_customized_enarmor 1 0.0516225
+rnp_tests.test_ffi_version 1 0.0532891
+rnp_tests.test_ffi_backend_version 1 0.0530111
+rnp_tests.test_ffi_key_export_customized_enarmor 1 0.39427
+rnp_tests.test_ffi_key_dump 1 0.0604133
+rnp_tests.test_ffi_key_dump_edge_cases 1 0.0504537
+rnp_tests.test_ffi_key_userid_dump_has_no_special_chars 1 0.146249
+rnp_tests.test_ffi_pkt_dump 1 0.0558848
+rnp_tests.test_ffi_rsa_v3_dump 1 0.0520718
+rnp_tests.test_ffi_load_userattr 1 0.0532674
+rnp_tests.test_ffi_revocations 1 0.0593614
+rnp_tests.test_ffi_file_output 1 0.0728863
+rnp_tests.test_ffi_stdout_output 1 0.0593097
+rnp_tests.test_ffi_import_keys_check_pktlen 1 0.0552982
+rnp_tests.test_ffi_calculate_iterations 1 0.21543
+rnp_tests.test_ffi_supported_features 1 0.0529641
+rnp_tests.test_ffi_output_to_armor 1 0.0565657
+rnp_tests.test_ffi_rnp_guess_contents 1 0.0534077
+rnp_tests.test_ffi_literal_filename 1 0.0881614
+rnp_tests.test_ffi_op_set_hash 1 0.0888381
+rnp_tests.test_ffi_op_set_compression 1 0.0899419
+rnp_tests.test_ffi_aead_params 1 0.525284
+rnp_tests.test_ffi_detached_verify_input 1 0.0587825
+rnp_tests.test_ffi_detached_cleartext_signed_input 1 0.0860166
+rnp_tests.test_ffi_op_verify_sig_count 1 0.286351
+rnp_tests.test_ffi_op_verify_get_protection_info 1 0.434788
+rnp_tests.test_ffi_op_verify_recipients_info 1 0.353337
+rnp_tests.test_ffi_secret_sig_import 1 0.116603
+rnp_tests.test_ffi_rnp_request_password 1 0.0551474
+rnp_tests.test_ffi_mdc_8k_boundary 1 0.121587
+rnp_tests.test_ffi_decrypt_wrong_mpi_bits 1 0.132672
+rnp_tests.test_ffi_decrypt_edge_cases 1 0.131205
+rnp_tests.test_ffi_key_remove 1 0.0848727
+rnp_tests.test_ffi_literal_packet 1 0.0553639
+rnp_tests.test_ffi_exception 1 0.0546118
+rnp_tests.test_ffi_key_protection_change 1 1.47754
+rnp_tests.test_ffi_set_log_fd 1 0.0528121
+rnp_tests.test_ffi_security_profile 1 0.0545962
+rnp_tests.test_result_to_string 1 0.0527323
+rnp_tests.test_ffi_wrong_hex_length 1 0.0524465
+rnp_tests.test_ffi_encrypt_pass 1 0.753094
+rnp_tests.test_ffi_encrypt_pass_provider 1 0.617646
+rnp_tests.test_ffi_encrypt_set_cipher 1 1.0805
+rnp_tests.test_ffi_encrypt_pk 1 0.0753
+rnp_tests.test_ffi_decrypt_pk_unlocked 1 0.419285
+rnp_tests.test_ffi_encrypt_pk_key_provider 1 0.0952935
+rnp_tests.test_ffi_encrypt_and_sign 1 0.433342
+rnp_tests.test_ffi_encrypt_pk_subkey_selection 1 0.101834
+rnp_tests.test_ffi_decrypt_small_rsa 1 0.0998147
+rnp_tests.test_ffi_decrypt_small_eg 1 0.157271
+rnp_tests.test_ffi_encrypt_no_wrap 1 0.111417
+rnp_tests.test_ffi_v5_signatures 1 0.0794383
+rnp_tests.test_ffi_mimemode_signature 1 0.0663755
+rnp_tests.test_ffi_uid_properties 1 0.0899315
+rnp_tests.test_ffi_uid_validity 1 0.0963514
+rnp_tests.test_ffi_remove_uid 1 0.0842163
+rnp_tests.test_ffi_key_signatures 1 0.0837253
+rnp_tests.test_ffi_import_signatures 1 0.0788825
+rnp_tests.test_ffi_export_revocation 1 0.274347
+rnp_tests.test_ffi_sig_validity 1 0.0738878
+rnp_tests.test_ffi_get_signature_type 1 0.0638204
+rnp_tests.test_ffi_remove_signature 1 0.0623069
+rnp_tests.test_ffi_remove_signatures 1 0.0973207
+rnp_tests.test_ffi_rsa_small_sig 1 0.0562124
+rnp_tests.test_ffi_key_critical_notations 1 0.060853
+rnp_tests.test_ffi_key_import_invalid_issuer 1 0.0552745
+rnp_tests.test_ffi_add_revoker_signature 1 0.275702
+rnp_tests.test_ffi_create_revocation_signature 1 0.381889
+rnp_tests.test_ffi_key_set_expiry_multiple_uids 1 0.228723
+rnp_tests.test_ffi_key_primary_uid_conflict 1 0.0521879
+rnp_tests.test_ffi_key_expired_certification_and_direct_sig 1 0.0560315
+rnp_tests.test_ffi_key_25519_tweaked_bits 1 0.0598532
+rnp_tests.test_ffi_key_revoke 1 0.241907
+rnp_tests.test_ffi_key_set_expiry 1 0.9013
+rnp_tests.test_ffi_key_get_protection_info 1 0.27357
+rnp_tests.test_ffi_key_default_subkey 1 0.069285
+rnp_tests.test_ffi_rnp_key_get_primary_grip 1 0.0573444
+rnp_tests.test_ffi_rnp_key_get_primary_fprint 1 0.0587336
+rnp_tests.test_ffi_keygen_json_pair 1 0.100727
+rnp_tests.test_ffi_keygen_json_pair_dsa_elg 1 1.2046
+rnp_tests.test_ffi_keygen_json_primary 1 0.0570528
+rnp_tests.test_ffi_keygen_json_sub 1 0.105896
+rnp_tests.test_ffi_keygen_json_edge_cases 1 0.481129
+rnp_tests.test_ffi_key_generate_misc 1 1.36571
+rnp_tests.test_ffi_sec_key_offline_operations 1 0.0578503
+rnp_tests.test_ffi_key_generate_rsa 1 0.450796
+rnp_tests.test_ffi_key_generate_dsa 1 1.64626
+rnp_tests.test_ffi_key_generate_ecdsa 1 0.0596838
+rnp_tests.test_ffi_key_generate_eddsa 1 0.0528663
+rnp_tests.test_ffi_key_generate_sm2 1 0.0638762
+rnp_tests.test_ffi_key_generate_ex 1 1.34571
+rnp_tests.test_ffi_key_generate_expiry_32bit 1 0.411246
+rnp_tests.test_ffi_key_generate_algnamecase 1 5.67423
+rnp_tests.test_ffi_key_generate_protection 1 2.10103
+rnp_tests.test_ffi_keygen_json_sub_pass_required 1 0.64867
+rnp_tests.test_ffi_key_to_json 1 0.0600828
+rnp_tests.test_ffi_key_iter 1 0.0595189
+rnp_tests.test_ffi_key_export 1 0.0682494
+rnp_tests.test_ffi_keys_import 1 0.140811
+rnp_tests.test_ffi_elgamal4096 1 0.0969759
+rnp_tests.test_ffi_malformed_keys_import 1 0.0974172
+rnp_tests.test_ffi_iterated_key_import 1 0.0849072
+rnp_tests.test_ffi_stripped_keys_import 1 0.0618674
+rnp_tests.test_ffi_key_import_edge_cases 1 0.0666304
+rnp_tests.test_ffi_key_import_gpg_s2k 1 0.171083
+rnp_tests.test_ffi_key_export_autocrypt 1 0.0843988
+rnp_tests.test_ffi_keys_import_autocrypt 1 0.0807794
+rnp_tests.test_ffi_keys_load_armored_spaces 1 0.0639208
+rnp_tests.test_ffi_sha1_self_signatures 1 0.0566181
+rnp_tests.test_reprotect_keys 1 0.169671
+rnp_tests.test_v5_keys 1 0.113168
+rnp_tests.test_v5_keys_g23 1 0.0564502
+rnp_tests.test_v5_sec_keys 1 0.174487
+rnp_tests.test_ffi_designated_revokers 1 0.100705
+rnp_tests.test_armored_keys_extra_line 1 0.0545483
+rnp_tests.test_rnp_mkstemp 1 0.0576969
+rnp_tests.test_rnp_access 1 0.0543398
+rnp_tests.rnpkeys_generatekey_testSignature 1 1.88765
+rnp_tests.rnpkeys_generatekey_testEncryption 1 0.897512
+rnp_tests.rnpkeys_generatekey_verifySupportedHashAlg 1 1.53802
+rnp_tests.rnpkeys_generatekey_verifyUserIdOption 1 0.816156
+rnp_tests.rnpkeys_generatekey_verifykeyHomeDirOption 1 0.266885
+rnp_tests.rnpkeys_generatekey_verifykeyKBXHomeDirOption 1 0.253346
+rnp_tests.rnpkeys_generatekey_verifykeyHomeDirNoPermission 1 0.156128
+rnp_tests.rnpkeys_generatekey_testExpertMode 1 3.81493
+rnp_tests.generatekeyECDSA_explicitlySetSmallOutputDigest_DigestAlgAdjusted 1 0.0674681
+rnp_tests.generatekey_multipleUserIds_ShouldFail 1 0.0532007
+rnp_tests.generatekeyECDSA_explicitlySetBiggerThanNeededDigest_ShouldSuceed 1 0.0640942
+rnp_tests.generatekeyECDSA_explicitlySetUnknownDigest_ShouldFail 1 0.0544535
+rnp_tests.test_generated_key_sigs 1 0.18236
+rnp_tests.test_kbx_nsigs 1 1.03666
+rnp_tests.test_key_add_userid 1 0.105604
+rnp_tests.key_grip 1 0.178417
+rnp_tests.test_key_prefs 1 0.085376
+rnp_tests.test_key_protect_load_pgp 1 0.786529
+rnp_tests.test_key_protect_sec_data 1 2.02161
+rnp_tests.test_key_store_search 1 0.086772
+rnp_tests.test_key_store_search_by_name 1 0.0843624
+rnp_tests.test_key_unlock_pgp 1 0.142326
+rnp_tests.test_key_validate 1 0.103531
+rnp_tests.test_forged_key_validate 1 0.102657
+rnp_tests.test_key_validity 1 0.0875055
+rnp_tests.test_key_expiry_direct_sig 1 0.201829
+rnp_tests.test_large_packet 1 20.5213
+rnp_tests.test_large_mpi_rsa_pub 1 0.176607
+rnp_tests.test_large_mpi_rsa_priv 1 9.62606
+rnp_tests.test_invalid_g10 1 0.0866966
+rnp_tests.test_load_g10 1 1.08319
+rnp_tests.test_load_g23 1 1.06343
+rnp_tests.test_invalid_kbx 1 0.068871
+rnp_tests.test_load_v3_keyring_pgp 1 0.0617961
+rnp_tests.test_load_v4_keyring_pgp 1 0.0715585
+rnp_tests.test_load_keyring_and_count_pgp 1 0.105306
+rnp_tests.test_load_check_bitfields_and_times 1 0.0920945
+rnp_tests.test_load_check_bitfields_and_times_v3 1 0.0799522
+rnp_tests.test_load_armored_pub_sec 1 0.0953846
+rnp_tests.test_load_merge 1 0.268064
+rnp_tests.test_load_public_from_secret 1 0.0899589
+rnp_tests.test_key_import 1 0.342799
+rnp_tests.test_load_subkey 1 0.08391
+rnp_tests.test_log_switch 1 0.0707515
+rnp_tests.test_partial_length_public_key 1 0.0614488
+rnp_tests.test_partial_length_signature 1 0.0892692
+rnp_tests.test_partial_length_first_packet_256 1 0.0876096
+rnp_tests.test_partial_length_zero_last_chunk 1 0.0874438
+rnp_tests.test_partial_length_largest 1 5.25782
+rnp_tests.test_partial_length_first_packet_length 1 0.0810939
+rnp_tests.test_pipe 1 0.0655857
+rnp_tests.test_pipe_source_error 1 0.0920565
+rnp_tests.test_pipe_dest_error 1 0.106324
+rnp_tests.test_rng_randomness 1 0.0624024
+rnp_tests.test_s2k_iterations 1 1.41568
+rnp_tests.test_stream_memory 1 0.0609506
+rnp_tests.test_stream_memory_discard 1 0.0587289
+rnp_tests.test_stream_file 1 0.0612378
+rnp_tests.test_stream_signatures 1 0.199259
+rnp_tests.test_stream_signatures_revoked_key 1 0.052128
+rnp_tests.test_stream_key_load 1 0.0555243
+rnp_tests.test_stream_key_load_errors 1 0.692865
+rnp_tests.test_stream_key_decrypt 1 0.491147
+rnp_tests.test_stream_key_encrypt 1 0.0651694
+rnp_tests.test_stream_key_signatures 1 0.0619741
+rnp_tests.test_stream_key_signature_validate 1 0.154242
+rnp_tests.test_stream_verify_no_key 1 0.0993917
+rnp_tests.test_y2k38 1 0.0569696
+rnp_tests.test_stream_dumper_y2k38 1 0.0517309
+rnp_tests.test_stream_dumper 1 0.0681261
+rnp_tests.test_stream_z 1 30.9159
+rnp_tests.test_stream_814_dearmor_double_free 1 0.0739404
+rnp_tests.test_stream_825_dearmor_blank_line 1 0.0689359
+rnp_tests.test_stream_dearmor_edge_cases 1 0.0539575
+rnp_tests.test_stream_deep_packet_nesting 1 1.94024
+rnp_tests.test_stream_cache 1 0.0552622
+rnp_tests.test_load_user_prefs 1 0.0838552
+rnp_tests.test_utils_hex2bin 1 0.107712
+rnp_tests.test_rnpcfg 1 0.0779768
+rnp_tests.test_rnpcfg_get_expiration 1 0.0595168
+rnp_tests.issue_1030_rnpkeys_secret_keys_unprotected 1 0.505222
+rnp_tests.test_issue_1115 1 0.0747748
+rnp_tests.test_issue_1171_key_import_and_remove 1 0.063471
+rnp_tests.test_sxp_depth 1 0.0555312
+rnp_tests.test_fuzz_keyring 1 6.54323
+rnp_tests.test_fuzz_keyring_g10 1 0.0573533
+rnp_tests.test_fuzz_keyring_kbx 1 0.0680579
+rnp_tests.test_fuzz_keyimport 1 5.97236
+rnp_tests.test_fuzz_sigimport 1 0.0686642
+rnp_tests.test_fuzz_dump 1 2.30632
+rnp_tests.test_fuzz_verify_detached 1 0.0784368
+rnp_tests.test_fuzz_verify 1 6.48424
+setupTestData 1 0.0687747
+cli_tests-SignDefault 1 19.551
+cli_tests-SignDSA 1 13.4998
+cli_tests-EncryptElgamal 1 6.53392
+cli_tests-Keystore 1 33.1293
+cli_tests-Encryption 1 85.3769
+cli_tests-Compression 1 30.5547
+cli_tests-EncryptEcdh 1 5.21128
+cli_tests-SignECDSA 1 5.14117
+cli_tests-EncryptSignRSA 1 3.70823
+cli_tests-Misc 1 46.4489
---
diff --git a/cmake/Modules/FindBotan.cmake b/cmake/Modules/FindBotan.cmake
new file mode 100644
index 0000000..4362b54
--- /dev/null
+++ b/cmake/Modules/FindBotan.cmake
@@ -0,0 +1,168 @@
+# Copyright (c) 2018-2020 Ribose Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+#.rst:
+# FindBotan
+# -----------
+#
+# Find the botan-2 or botan-3 library.
+#
+# IMPORTED Targets
+# ^^^^^^^^^^^^^^^^
+#
+# This module defines :prop_tgt:`IMPORTED` targets:
+#
+# ``Botan::Botan``
+# The botan-2 or botan-3 library, if found.
+#
+# Result variables
+# ^^^^^^^^^^^^^^^^
+#
+# This module defines the following variables:
+#
+# ::
+#
+# BOTAN_FOUND - true if the headers and library were found
+# BOTAN_INCLUDE_DIRS - where to find headers
+# BOTAN_LIBRARIES - list of libraries to link
+# BOTAN_VERSION - library version that was found, if any
+#
+# Hints
+# ^^^^^
+#
+# These variables may be set to control search behaviour:
+#
+# ``BOTAN_ROOT_DIR``
+# Set to the root directory of the Botan installation.
+#
+
+# use pkg-config to get the directories and then use these values
+# in the find_path() and find_library() calls
+
+find_package(PkgConfig QUIET)
+
+# Search for the version 2 first unless version 3 requested
+if(NOT "${Botan_FIND_VERSION_MAJOR}" EQUAL "3")
+ pkg_check_modules(PC_BOTAN QUIET botan-2)
+ set(_suffixes "botan-2" "botan-3")
+ set(_names "botan-2" "libbotan-2" "botan-3" "libbotan-3")
+else()
+ set(_suffixes "botan-3")
+ set(_names "botan-3" "libbotan-3")
+endif()
+if(NOT PC_BOTAN_FOUND)
+ pkg_check_modules(PC_BOTAN QUIET botan-3)
+endif()
+
+if(DEFINED BOTAN_ROOT_DIR)
+ set(_hints_include "${BOTAN_ROOT_DIR}/include")
+ set(_hints_lib "${BOTAN_ROOT_DIR}/lib")
+endif()
+if(DEFINED ENV{BOTAN_ROOT_DIR})
+ list(APPEND _hints_include "$ENV{BOTAN_ROOT_DIR}/include")
+ list(APPEND _hints_lib "$ENV{BOTAN_ROOT_DIR}/lib")
+endif()
+
+# Append PC_* stuff only if BOTAN_ROOT_DIR is not specified
+if(NOT _hints_include)
+ list(APPEND _hints_include ${PC_BOTAN_INCLUDEDIR} ${PC_BOTAN_INCLUDE_DIRS})
+ list(APPEND _hints_lib ${PC_BOTAN_LIBDIR} ${PC_BOTAN_LIBRARY_DIRS})
+else()
+ set(_no_def_path "NO_DEFAULT_PATH")
+endif()
+
+# find the headers
+find_path(BOTAN_INCLUDE_DIR
+ NAMES botan/version.h
+ HINTS
+ ${_hints_include}
+ PATH_SUFFIXES ${_suffixes}
+ ${_no_def_path}
+)
+
+# find the library
+if(MSVC)
+ find_library(BOTAN_LIBRARY
+ NAMES botan ${_names}
+ HINTS
+ ${_hints_lib}
+ ${_no_def_path}
+ )
+else()
+ find_library(BOTAN_LIBRARY
+ NAMES
+ ${_names}
+ HINTS
+ ${_hints_lib}
+ ${_no_def_path}
+ )
+endif()
+
+# determine the version
+if(BOTAN_INCLUDE_DIR AND EXISTS "${BOTAN_INCLUDE_DIR}/botan/build.h")
+ file(STRINGS "${BOTAN_INCLUDE_DIR}/botan/build.h" botan_version_str
+ REGEX "^#define[\t ]+(BOTAN_VERSION_[A-Z]+)[\t ]+[0-9]+")
+
+ string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_MAJOR[\t ]+([0-9]+).*"
+ "\\1" _botan_version_major "${botan_version_str}")
+ string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_MINOR[\t ]+([0-9]+).*"
+ "\\1" _botan_version_minor "${botan_version_str}")
+ string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_PATCH[\t ]+([0-9]+).*"
+ "\\1" _botan_version_patch "${botan_version_str}")
+ set(BOTAN_VERSION "${_botan_version_major}.${_botan_version_minor}.${_botan_version_patch}"
+ CACHE INTERNAL "The version of Botan which was detected")
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Botan
+ REQUIRED_VARS BOTAN_LIBRARY BOTAN_INCLUDE_DIR
+ VERSION_VAR BOTAN_VERSION
+)
+
+if (BOTAN_FOUND)
+ set(BOTAN_INCLUDE_DIRS ${BOTAN_INCLUDE_DIR} ${PC_BOTAN_INCLUDE_DIRS})
+ set(BOTAN_LIBRARIES ${BOTAN_LIBRARY})
+endif()
+
+if (BOTAN_FOUND AND NOT TARGET Botan::Botan)
+ # create the new library target
+ add_library(Botan::Botan UNKNOWN IMPORTED)
+ # set the required include dirs for the target
+ if (BOTAN_INCLUDE_DIRS)
+ set_target_properties(Botan::Botan
+ PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${BOTAN_INCLUDE_DIRS}"
+ )
+ endif()
+ # set the required libraries for the target
+ if (EXISTS "${BOTAN_LIBRARY}")
+ set_target_properties(Botan::Botan
+ PROPERTIES
+ IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+ IMPORTED_LOCATION "${BOTAN_LIBRARY}"
+ )
+ endif()
+endif()
+
+mark_as_advanced(BOTAN_INCLUDE_DIR BOTAN_LIBRARY)
diff --git a/cmake/Modules/FindBotan2.cmake b/cmake/Modules/FindBotan2.cmake
deleted file mode 100644
index 2708491..0000000
--- a/cmake/Modules/FindBotan2.cmake
+++ /dev/null
@@ -1,131 +0,0 @@
-# Copyright (c) 2018-2020 Ribose Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
-# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-
-#.rst:
-# FindBotan2
-# -----------
-#
-# Find the botan-2 library.
-#
-# IMPORTED Targets
-# ^^^^^^^^^^^^^^^^
-#
-# This module defines :prop_tgt:`IMPORTED` targets:
-#
-# ``Botan2::Botan2``
-# The botan-2 library, if found.
-#
-# Result variables
-# ^^^^^^^^^^^^^^^^
-#
-# This module defines the following variables:
-#
-# ::
-#
-# BOTAN2_FOUND - true if the headers and library were found
-# BOTAN2_INCLUDE_DIRS - where to find headers
-# BOTAN2_LIBRARIES - list of libraries to link
-# BOTAN2_VERSION - library version that was found, if any
-
-# use pkg-config to get the directories and then use these values
-# in the find_path() and find_library() calls
-find_package(PkgConfig QUIET)
-pkg_check_modules(PC_BOTAN2 QUIET botan-2)
-
-# find the headers
-find_path(BOTAN2_INCLUDE_DIR
- NAMES botan/version.h
- HINTS
- ${PC_BOTAN2_INCLUDEDIR}
- ${PC_BOTAN2_INCLUDE_DIRS}
- PATH_SUFFIXES botan-2
-)
-
-# find the library
-if(MSVC)
- find_library(BOTAN2_LIBRARY
- NAMES botan
- HINTS
- ${PC_BOTAN2_LIBDIR}
- ${PC_BOTAN2_LIBRARY_DIRS}
- )
-else()
- find_library(BOTAN2_LIBRARY
- NAMES botan-2 libbotan-2
- HINTS
- ${PC_BOTAN2_LIBDIR}
- ${PC_BOTAN2_LIBRARY_DIRS}
- )
-endif()
-
-# determine the version
-if(PC_BOTAN2_VERSION)
- set(BOTAN2_VERSION ${PC_BOTAN2_VERSION})
-elseif(BOTAN2_INCLUDE_DIR AND EXISTS "${BOTAN2_INCLUDE_DIR}/botan/build.h")
- file(STRINGS "${BOTAN2_INCLUDE_DIR}/botan/build.h" botan2_version_str
- REGEX "^#define[\t ]+(BOTAN_VERSION_[A-Z]+)[\t ]+[0-9]+")
-
- string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_MAJOR[\t ]+([0-9]+).*"
- "\\1" _botan2_version_major "${botan2_version_str}")
- string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_MINOR[\t ]+([0-9]+).*"
- "\\1" _botan2_version_minor "${botan2_version_str}")
- string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_PATCH[\t ]+([0-9]+).*"
- "\\1" _botan2_version_patch "${botan2_version_str}")
- set(BOTAN2_VERSION "${_botan2_version_major}.${_botan2_version_minor}.${_botan2_version_patch}"
- CACHE INTERNAL "The version of Botan which was detected")
-endif()
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(Botan2
- REQUIRED_VARS BOTAN2_LIBRARY BOTAN2_INCLUDE_DIR
- VERSION_VAR BOTAN2_VERSION
-)
-
-if (BOTAN2_FOUND)
- set(BOTAN2_INCLUDE_DIRS ${BOTAN2_INCLUDE_DIR} ${PC_BOTAN2_INCLUDE_DIRS})
- set(BOTAN2_LIBRARIES ${BOTAN2_LIBRARY})
-endif()
-
-if (BOTAN2_FOUND AND NOT TARGET Botan2::Botan2)
- # create the new library target
- add_library(Botan2::Botan2 UNKNOWN IMPORTED)
- # set the required include dirs for the target
- if (BOTAN2_INCLUDE_DIRS)
- set_target_properties(Botan2::Botan2
- PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${BOTAN2_INCLUDE_DIRS}"
- )
- endif()
- # set the required libraries for the target
- if (EXISTS "${BOTAN2_LIBRARY}")
- set_target_properties(Botan2::Botan2
- PROPERTIES
- IMPORTED_LINK_INTERFACE_LANGUAGES "C"
- IMPORTED_LOCATION "${BOTAN2_LIBRARY}"
- )
- endif()
-endif()
-
-mark_as_advanced(BOTAN2_INCLUDE_DIR BOTAN2_LIBRARY)
-
diff --git a/cmake/Modules/FindOpenSSLFeatures.cmake b/cmake/Modules/FindOpenSSLFeatures.cmake
index 6967764..c3e3e33 100644
--- a/cmake/Modules/FindOpenSSLFeatures.cmake
+++ b/cmake/Modules/FindOpenSSLFeatures.cmake
@@ -74,7 +74,6 @@ file(WRITE "${_fossl_work_dir}/CMakeLists.txt"
"cmake_minimum_required(VERSION 3.18)\n\
project(findopensslfeatures LANGUAGES C)\n\
set(CMAKE_C_STANDARD 99)\n\
-include(FindOpenSSL)\n\
find_package(OpenSSL REQUIRED)\n\
add_executable(findopensslfeatures findopensslfeatures.c)\n\
target_include_directories(findopensslfeatures PRIVATE ${OPENSSL_INCLUDE_DIR})\n\
@@ -139,7 +138,7 @@ else(WIN32 AND NOT MINGW)
set(FOF "build/findopensslfeatures")
endif(WIN32 AND NOT MINGW)
-foreach(feature "hashes" "ciphers" "curves" "publickey")
+foreach(feature "hashes" "ciphers" "curves" "publickey" "providers")
execute_process(
COMMAND "${FOF}" "${feature}"
WORKING_DIRECTORY "${_fossl_work_dir}"
@@ -160,7 +159,7 @@ foreach(feature "hashes" "ciphers" "curves" "publickey")
list(APPEND OPENSSL_SUPPORTED_FEATURES ${OPENSSL_SUPPORTED_${feature_up}})
endforeach()
-message(STATUS "Fetched OpenSSL features: ${hashes_len} hashes, ${ciphers_len} ciphers, ${curves_len} curves, ${publickey_len} publickey.")
+message(STATUS "Fetched OpenSSL features: ${hashes_len} hashes, ${ciphers_len} ciphers, ${curves_len} curves, ${publickey_len} publickey, ${providers_len} providers.")
function(OpenSSLHasFeature FEATURE VARIABLE)
string(TOUPPER ${FEATURE} _feature_up)
diff --git a/cmake/Modules/findopensslfeatures.c b/cmake/Modules/findopensslfeatures.c
index 390f1d2..ed7eb8e 100644
--- a/cmake/Modules/findopensslfeatures.c
+++ b/cmake/Modules/findopensslfeatures.c
@@ -4,6 +4,9 @@
#include <openssl/ec.h>
#include <openssl/objects.h>
#include <openssl/evp.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/provider.h>
+#endif
int
list_curves()
@@ -65,15 +68,57 @@ list_ciphers()
return 0;
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+static void
+print_km_name(const char *name, void *param)
+{
+ /* Do not print OIDs for better clarity */
+ if (!name || ((name[0] <= '9') && (name[0] >= '0'))) {
+ return;
+ }
+ printf("%s\n", name);
+}
+
+static void
+print_km(EVP_KEYMGMT *km, void *param)
+{
+ EVP_KEYMGMT_names_do_all(km, print_km_name, NULL);
+}
+#endif
+
int
list_publickey()
{
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
for (size_t i = 0; i < EVP_PKEY_meth_get_count(); i++) {
const EVP_PKEY_METHOD *pmeth = EVP_PKEY_meth_get0(i);
int id = 0;
EVP_PKEY_meth_get0_info(&id, NULL, pmeth);
printf("%s\n", OBJ_nid2ln(id));
}
+#else
+ EVP_KEYMGMT_do_all_provided(NULL, print_km, NULL);
+#endif
+ return 0;
+}
+
+int
+list_providers()
+{
+ printf("default\n");
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ const char *known_names[] = {"legacy", "fips"};
+ for (size_t i = 0; i < sizeof(known_names) / sizeof(known_names[0]); i++) {
+ OSSL_PROVIDER *prov = OSSL_PROVIDER_load(NULL, known_names[i]);
+ if (prov) {
+ printf("%s\n", known_names[i]);
+ OSSL_PROVIDER_unload(prov);
+ }
+ }
+#else
+ /* OpenSSL < 3.0 includes all legacy algorithms in the default provider */
+ printf("legacy\n");
+#endif
return 0;
}
@@ -81,7 +126,8 @@ int
main(int argc, char *argv[])
{
if (argc != 2) {
- fprintf(stderr, "Usage: opensslfeatures [curves|hashes|ciphers|publickey]\n");
+ fprintf(stderr,
+ "Usage: opensslfeatures [curves|hashes|ciphers|publickey|providers]\n");
return 1;
}
if (!strcmp(argv[1], "hashes")) {
@@ -96,6 +142,9 @@ main(int argc, char *argv[])
if (!strcmp(argv[1], "publickey")) {
return list_publickey();
}
+ if (!strcmp(argv[1], "providers")) {
+ return list_providers();
+ }
fprintf(stderr, "Unknown command: %s\n", argv[1]);
return 1;
}
diff --git a/cmake/packaging.cmake b/cmake/packaging.cmake
index 2180845..edea2a7 100644
--- a/cmake/packaging.cmake
+++ b/cmake/packaging.cmake
@@ -31,6 +31,7 @@ set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PACKAGE_DESCRIPTION_SHORT}")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_FILE_NAME "rnp-${CPACK_PACKAGE_VERSION}")
set(CPACK_PACKAGE_NAME "rnp${PROJECT_VERSION_MAJOR}")
+set(CPACK_SOURCE_PACKAGE_FILE_NAME "rnp-v${CPACK_PACKAGE_VERSION}")
set(CPACK_SOURCE_IGNORE_FILES "/installs/;/build/;/\\\\.git/;\\\\.#;/#")
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..52dbce8
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,27 @@
+#
+# Copyright 2024 Ribose Inc. (https://www.ribose.com)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy of
+# this software and associated documentation files (the "Software"), to deal in
+# the Software without restriction, including without limitation the rights to
+# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+# the Software, and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+ignore:
+# Google test sources stored under the build directory
+ - "build"
+ - "src/examples"
+ - "src/libsexpp"
+ - "src/tests"
diff --git a/docs/develop.adoc b/docs/develop.adoc
index e0c0e41..28f0617 100644
--- a/docs/develop.adoc
+++ b/docs/develop.adoc
@@ -199,7 +199,7 @@ to maintain a consistent code base.
=== Code Formatting
-`clang-format` (v9.0.0) can be used to format the code base, utilizing
+`clang-format` (v11.0.0) can be used to format the code base, utilizing
the `.clang-format` file included in the repository.
==== clang-format git hook
@@ -213,7 +213,7 @@ cd rnp
git-hooks/enable.sh
--
-If you do not have clang-format v9.0.0 available, you can use a docker
+If you do not have clang-format v11.0.0 available, you can use a docker
container for this purpose by setting `USE_DOCKER="yes"` in
`git-hooks/pre-commit.sh`.
diff --git a/docs/installation.adoc b/docs/installation.adoc
index 991fa85..6ca6856 100644
--- a/docs/installation.adoc
+++ b/docs/installation.adoc
@@ -54,7 +54,7 @@ Prerequisites: please ensure `git` is installed on the system
[source,console]
----
# Clone the repository by version tag (or omit it to get the latest sources)
-git clone https://github.com/rnpgp/rnp.git -b v0.17.0
+git clone https://github.com/rnpgp/rnp.git -b v0.17.1
Please ensure that you clone with submodules if you use a version higher then 0.16.2
git clone https://github.com/rnpgp/rnp.git --recurse-submodules --shallow-submodules
@@ -63,8 +63,8 @@ git clone https://github.com/rnpgp/rnp.git --recurse-submodules --shallow-submod
sudo apt install g++-8 cmake libbz2-dev zlib1g-dev libjson-c-dev build-essential python-minimal
# Download, build and install Botan2
-wget -qO- https://botan.randombit.net/releases/Botan-2.18.2.tar.xz | tar xvJ
-cd Botan-2.18.2
+wget -qO- https://botan.randombit.net/releases/Botan-2.19.4.tar.xz | tar xvJ
+cd Botan-2.19.4
./configure.py --prefix=/usr
make
sudo make install
@@ -91,7 +91,7 @@ Prerequisite: please ensure `git` is installed on the system.
[source,console]
----
# Clone the repository by version tag (or omit it to get the latest sources)
-git clone https://github.com/rnpgp/rnp.git -b v0.17.0
+git clone https://github.com/rnpgp/rnp.git -b v0.17.1
Please ensure that you clone with submodules if you use a version higher then 0.16.2
git clone https://github.com/rnpgp/rnp.git --recurse-submodules --shallow-submodules
@@ -116,28 +116,11 @@ sudo make install
== On Gentoo Linux
-RNP ebuilds are available from an overlay repository named `rnp`.
-
-=== Using eselect-repository (the current way)
-
-Prerequisite: ensure `eselect-repository` is installed on your system.
-
-[source,console]
-----
-eselect repository enable rnp
-emaint sync -r rnp
-emerge -av app-crypt/rnp
-----
-
-=== Using layman (the old way)
-
-Prerequisite: ensure `layman` is installed on your system.
+RNP is present in the official Gentoo repository under the name `dev-util/librnp`.
[source,console]
----
-layman -a rnp
-layman -s rnp
-emerge -av app-crypt/rnp
+emerge -av dev-util/librnp
----
== Compile from source
diff --git a/git-hooks/pre-commit.sh b/git-hooks/pre-commit.sh
index 5cab632..d69e452 100755
--- a/git-hooks/pre-commit.sh
+++ b/git-hooks/pre-commit.sh
@@ -17,7 +17,7 @@ INTERACTIVE="yes"
# Leave this stuff
STASH_NAME="pre-commit-$(date +%s)"
CONTAINER_NAME="clang-format-$(date +%s)"
-CLANG_FORMAT_VERSION="11.1.0"
+CLANG_FORMAT_VERSION="11.0.0"
apply_patch() {
local patchfile=$1
diff --git a/include/rnp/rnp.h b/include/rnp/rnp.h
index 6667169..149264d 100644
--- a/include/rnp/rnp.h
+++ b/include/rnp/rnp.h
@@ -139,6 +139,13 @@ typedef uint32_t rnp_result_t;
#define RNP_VERIFY_ALLOW_HIDDEN_RECIPIENT (1U << 2)
/**
+ * Key feature flags.
+ */
+#define RNP_KEY_FEATURE_MDC (1U << 0)
+#define RNP_KEY_FEATURE_AEAD (1U << 1)
+#define RNP_KEY_FEATURE_V5 (1U << 2)
+
+/**
* Return a constant string describing the result code
*/
RNP_API const char *rnp_result_to_string(rnp_result_t result);
@@ -305,8 +312,8 @@ typedef bool (*rnp_password_cb)(rnp_ffi_t ffi,
/** callback used to signal the application that a key is needed
*
- * The application should use the appropriate functions (rnp_load_public_keys, etc)
- * to load the requested key.
+ * The application should use the appropriate functions (rnp_load_keys() or
+ * rnp_import_keys()) to load the requested key.
*
* This may be called multiple times for the same key. For example, if attempting
* to verify a signature, the signer's keyid may be used first to request the key.
@@ -323,7 +330,7 @@ typedef bool (*rnp_password_cb)(rnp_ffi_t ffi,
* the keyrings.
*
* @param ffi
- * @param app_ctx provided by application in rnp_keyring_open
+ * @param app_ctx provided by application in rnp_ffi_set_key_provider()
* @param identifier_type the type of identifier ("userid", "keyid", "grip")
* @param identifier the identifier for locating the key
* @param secret true if a secret key is being requested
@@ -1443,6 +1450,9 @@ RNP_API rnp_result_t rnp_signature_get_creation(rnp_signature_handle_t sig, uint
RNP_API rnp_result_t rnp_signature_get_expiration(rnp_signature_handle_t sig,
uint32_t * expires);
+RNP_API rnp_result_t rnp_signature_get_features(rnp_signature_handle_t sig,
+ uint32_t * features);
+
/** Get signer's key id from the signature.
* Note: if key id is not available from the signature then NULL value will
* be stored to result.
diff --git a/src/common/uniwin.h b/src/common/uniwin.h
index 095c325..b7e2b52 100644
--- a/src/common/uniwin.h
+++ b/src/common/uniwin.h
@@ -35,9 +35,9 @@
#include <direct.h> /* for _getcwd() and _chdir() */
#ifdef _WIN64
-#define ssize_t __int64
+typedef __int64 ssize_t;
#else
-#define ssize_t long
+typedef long ssize_t;
#endif
#define STDIN_FILENO 0
diff --git a/src/fuzzing/keyring.c b/src/fuzzing/keyring.c
index bac4e13..ad16cd0 100644
--- a/src/fuzzing/keyring.c
+++ b/src/fuzzing/keyring.c
@@ -35,15 +35,14 @@ int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
#endif
{
- rnp_input_t input = NULL;
- rnp_result_t ret = 0;
- rnp_ffi_t ffi = NULL;
+ rnp_input_t input = NULL;
+ rnp_ffi_t ffi = NULL;
- ret = rnp_input_from_memory(&input, data, size, false);
+ (void) rnp_input_from_memory(&input, data, size, false);
- ret = rnp_ffi_create(&ffi, "GPG", "GPG");
- ret =
- rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS);
+ (void) rnp_ffi_create(&ffi, "GPG", "GPG");
+ (void) rnp_load_keys(
+ ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS);
rnp_input_destroy(input);
rnp_ffi_destroy(ffi);
diff --git a/src/fuzzing/keyring_kbx.c b/src/fuzzing/keyring_kbx.c
index 768e669..7c9ce60 100644
--- a/src/fuzzing/keyring_kbx.c
+++ b/src/fuzzing/keyring_kbx.c
@@ -35,14 +35,12 @@ int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
#endif
{
- rnp_input_t input = NULL;
- rnp_result_t ret = 0;
- rnp_ffi_t ffi = NULL;
+ rnp_input_t input = NULL;
+ rnp_ffi_t ffi = NULL;
- ret = rnp_input_from_memory(&input, data, size, false);
-
- ret = rnp_ffi_create(&ffi, "KBX", "G10");
- ret = rnp_load_keys(ffi, "KBX", input, RNP_LOAD_SAVE_PUBLIC_KEYS);
+ (void) rnp_input_from_memory(&input, data, size, false);
+ (void) rnp_ffi_create(&ffi, "KBX", "G10");
+ (void) rnp_load_keys(ffi, "KBX", input, RNP_LOAD_SAVE_PUBLIC_KEYS);
rnp_input_destroy(input);
rnp_ffi_destroy(ffi);
diff --git a/src/fuzzing/sigimport.c b/src/fuzzing/sigimport.c
index 35adeb7..89bd01a 100644
--- a/src/fuzzing/sigimport.c
+++ b/src/fuzzing/sigimport.c
@@ -35,14 +35,13 @@ int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
#endif
{
- rnp_input_t input = NULL;
- rnp_result_t ret = 0;
- rnp_ffi_t ffi = NULL;
+ rnp_input_t input = NULL;
+ rnp_ffi_t ffi = NULL;
- ret = rnp_input_from_memory(&input, data, size, false);
- ret = rnp_ffi_create(&ffi, "GPG", "GPG");
+ (void) rnp_input_from_memory(&input, data, size, false);
+ (void) rnp_ffi_create(&ffi, "GPG", "GPG");
char *results = NULL;
- ret = rnp_import_signatures(ffi, input, 0, &results);
+ (void) rnp_import_signatures(ffi, input, 0, &results);
rnp_buffer_destroy(results);
rnp_input_destroy(input);
rnp_ffi_destroy(ffi);
diff --git a/src/fuzzing/verify.c b/src/fuzzing/verify.c
index cd6c849..7dcfb01 100644
--- a/src/fuzzing/verify.c
+++ b/src/fuzzing/verify.c
@@ -39,16 +39,15 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
rnp_ffi_t ffi = NULL;
rnp_input_t input = NULL;
rnp_output_t output = NULL;
- rnp_result_t ret;
- ret = rnp_ffi_create(&ffi, "GPG", "GPG");
- ret = rnp_input_from_memory(&input, data, size, false);
- ret = rnp_output_to_null(&output);
+ (void) rnp_ffi_create(&ffi, "GPG", "GPG");
+ (void) rnp_input_from_memory(&input, data, size, false);
+ (void) rnp_output_to_null(&output);
rnp_op_verify_t op = NULL;
- ret = rnp_op_verify_create(&op, ffi, input, output);
- ret = rnp_op_verify_execute(op);
- ret = rnp_op_verify_destroy(op);
+ (void) rnp_op_verify_create(&op, ffi, input, output);
+ (void) rnp_op_verify_execute(op);
+ (void) rnp_op_verify_destroy(op);
rnp_input_destroy(input);
rnp_output_destroy(output);
diff --git a/src/fuzzing/verify_detached.c b/src/fuzzing/verify_detached.c
index 2afb59a..316e7a1 100644
--- a/src/fuzzing/verify_detached.c
+++ b/src/fuzzing/verify_detached.c
@@ -36,20 +36,19 @@ int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
#endif
{
- rnp_ffi_t ffi = NULL;
- rnp_input_t input = NULL;
- rnp_input_t msg_input = NULL;
- rnp_result_t ret;
+ rnp_ffi_t ffi = NULL;
+ rnp_input_t input = NULL;
+ rnp_input_t msg_input = NULL;
- ret = rnp_ffi_create(&ffi, "GPG", "GPG");
- ret = rnp_input_from_memory(&input, data, size, false);
+ (void) rnp_ffi_create(&ffi, "GPG", "GPG");
+ (void) rnp_input_from_memory(&input, data, size, false);
const char *msg = "message";
- ret = rnp_input_from_memory(&msg_input, (const uint8_t *) msg, strlen(msg), true);
+ (void) rnp_input_from_memory(&msg_input, (const uint8_t *) msg, strlen(msg), true);
rnp_op_verify_t verify = NULL;
- ret = rnp_op_verify_detached_create(&verify, ffi, msg_input, input);
- ret = rnp_op_verify_execute(verify);
- ret = rnp_op_verify_destroy(verify);
+ (void) rnp_op_verify_detached_create(&verify, ffi, msg_input, input);
+ (void) rnp_op_verify_execute(verify);
+ (void) rnp_op_verify_destroy(verify);
rnp_input_destroy(input);
rnp_input_destroy(msg_input);
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index 086ac57..3b4b444 100755
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -31,11 +31,15 @@ find_package(ZLIB REQUIRED)
# required packages
find_package(JSON-C 0.11 REQUIRED)
-if (CRYPTO_BACKEND_BOTAN)
- find_package(Botan2 2.14.0 REQUIRED)
+if (CRYPTO_BACKEND_BOTAN3)
+ find_package(Botan 3.0.0 REQUIRED)
+elseif (CRYPTO_BACKEND_BOTAN)
+ find_package(Botan 2.14.0 REQUIRED)
+ if(BOTAN_VERSION VERSION_GREATER_EQUAL 3.0.0)
+ set(CRYPTO_BACKEND_BOTAN3 1)
+ endif()
endif()
if (CRYPTO_BACKEND_OPENSSL)
- include(FindOpenSSL)
find_package(OpenSSL 1.1.1 REQUIRED)
include(FindOpenSSLFeatures)
if("${OPENSSL_VERSION}" VERSION_GREATER_EQUAL "3.0.0")
@@ -43,6 +47,10 @@ if (CRYPTO_BACKEND_OPENSSL)
endif()
endif()
+if(CRYPTO_BACKEND_BOTAN3)
+ set(CMAKE_CXX_STANDARD 20)
+endif()
+
# generate a config.h
include(CheckIncludeFileCXX)
include(CheckCXXSymbolExists)
@@ -75,7 +83,7 @@ function(backend_has_feature FEATURE RESULT_VARNAME)
if (CRYPTO_BACKEND_LOWERCASE STREQUAL "botan")
check_cxx_symbol_exists("BOTAN_HAS_${FEATURE}" botan/build.h ${RESULT_VARNAME})
else()
- message(STATUS "Looking for OpenSSL feature ${FEATURE}")
+ message(STATUS "Looking for OpenSSL feature ${FEATURE}")
OpenSSLHasFeature(${FEATURE} ${RESULT_VARNAME})
if (${RESULT_VARNAME})
message(STATUS "Looking for OpenSSL feature ${FEATURE} - found")
@@ -100,7 +108,7 @@ function(resolve_feature_state RNP_FEATURE BACKEND_FEATURES)
foreach(feature ${BACKEND_FEATURES})
backend_has_feature("${feature}" _has_${feature})
- if (NOT ${_has_${feature}})
+ if (NOT _has_${feature})
set(${RNP_FEATURE} Off CACHE STRING "Autodetected" FORCE)
message(${MESSAGE_TYPE} "${RNP_FEATURE} requires ${CRYPTO_BACKEND} feature which is missing: ${feature}. ${OUTCOME}.")
return()
@@ -129,7 +137,7 @@ endfunction()
if(CRYPTO_BACKEND_BOTAN)
# check botan's enabled features
- set(CMAKE_REQUIRED_INCLUDES "${BOTAN2_INCLUDE_DIRS}")
+ set(CMAKE_REQUIRED_INCLUDES "${BOTAN_INCLUDE_DIRS}")
set(_botan_required_features
# base
BIGINT FFI HEX_CODEC PGP_S2K
@@ -142,12 +150,17 @@ if(CRYPTO_BACKEND_BOTAN)
# hash
CRC24 HASH MD5 SHA1 SHA2_32 SHA2_64 SHA3
# public-key core
- DL_GROUP DL_PUBLIC_KEY_FAMILY ECC_GROUP ECC_PUBLIC_KEY_CRYPTO PUBLIC_KEY_CRYPTO
+ DL_GROUP ECC_GROUP ECC_PUBLIC_KEY_CRYPTO PUBLIC_KEY_CRYPTO # Botan-2: DL_PUBLIC_KEY_FAMILY Botan-3: DL_SCHEME, see switch below
# public-key algs
CURVE_25519 DSA ECDH ECDSA ED25519 ELGAMAL RSA
# public-key operations etc
EME_PKCS1v15 EMSA_PKCS1 EMSA_RAW KDF_BASE RFC3394_KEYWRAP SP800_56A
)
+ if(BOTAN_VERSION VERSION_LESS 3.0.0)
+ set(_botan_required_features ${_botan_required_features} DL_PUBLIC_KEY_FAMILY)
+ else()
+ set(_botan_required_features ${_botan_required_features} DL_SCHEME RAW_HASH_FN)
+ endif()
foreach(feature ${_botan_required_features})
check_cxx_symbol_exists("BOTAN_HAS_${feature}" botan/build.h _botan_has_${feature})
if (NOT _botan_has_${feature})
@@ -181,19 +194,26 @@ if(CRYPTO_BACKEND_OPENSSL)
RSAENCRYPTION DSAENCRYPTION DHKEYAGREEMENT ID-ECPUBLICKEY X25519 ED25519
)
foreach(feature ${_openssl_required_features})
- message(STATUS "Looking for OpenSSL feature ${feature}")
- OpenSSLHasFeature("${feature}" _openssl_has_${feature})
+ backend_has_feature("${feature}" _openssl_has_${feature})
if (NOT _openssl_has_${feature})
message(FATAL_ERROR "A required OpenSSL feature is missing: ${feature}")
endif()
- message(STATUS "Looking for OpenSSL feature ${feature} - found")
endforeach()
+ if (CRYPTO_BACKEND_OPENSSL3)
+ backend_has_feature("LEGACY" CRYPTO_BACKEND_OPENSSL3_LEGACY)
+ endif()
+
resolve_feature_state(ENABLE_BRAINPOOL "BRAINPOOLP256R1;BRAINPOOLP384R1;BRAINPOOLP512R1")
- resolve_feature_state(ENABLE_IDEA "IDEA-ECB;IDEA-CBC")
- resolve_feature_state(ENABLE_BLOWFISH "BF-ECB")
- resolve_feature_state(ENABLE_CAST5 "CAST5-ECB")
- resolve_feature_state(ENABLE_RIPEMD160 "RIPEMD160")
+ # Not all of the OpenSSL installations have legacy crypto provider
+ resolve_feature_state(ENABLE_IDEA "IDEA-ECB;IDEA-CBC;LEGACY")
+ resolve_feature_state(ENABLE_BLOWFISH "BF-ECB;LEGACY")
+ resolve_feature_state(ENABLE_CAST5 "CAST5-ECB;LEGACY")
+ if("${OPENSSL_VERSION}" VERSION_GREATER_EQUAL "3.0.7")
+ resolve_feature_state(ENABLE_RIPEMD160 "RIPEMD160")
+ else()
+ resolve_feature_state(ENABLE_RIPEMD160 "RIPEMD160;LEGACY")
+ endif()
resolve_feature_state(ENABLE_AEAD "AES-128-OCB;AES-192-OCB;AES-256-OCB")
openssl_nope(ENABLE_SM2 "it's on our roadmap, see https://github.com/rnpgp/rnp/issues/1877")
#resolve_feature_state(ENABLE_SM2 "SM2;SM3;SM4-ECB")
@@ -320,15 +340,16 @@ target_include_directories(librnp-obj
PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}"
"${PROJECT_SOURCE_DIR}/src"
+ "${SEXPP_INCLUDE_DIRS}"
)
target_link_libraries(librnp-obj PRIVATE JSON-C::JSON-C)
if (CRYPTO_BACKEND_BOTAN)
- target_link_libraries(librnp-obj PRIVATE Botan2::Botan2)
+ target_link_libraries(librnp-obj PRIVATE Botan::Botan)
elseif (CRYPTO_BACKEND_OPENSSL)
target_link_libraries(librnp-obj PRIVATE OpenSSL::Crypto)
endif()
-target_link_libraries(librnp-obj PRIVATE sexp)
+target_link_libraries(librnp-obj PRIVATE sexpp)
set_target_properties(librnp-obj PROPERTIES CXX_VISIBILITY_PRESET hidden)
if (TARGET BZip2::BZip2)
@@ -384,7 +405,7 @@ foreach (prop LINK_LIBRARIES INTERFACE_LINK_LIBRARIES INCLUDE_DIRECTORIES INTERF
get_target_property(val librnp-obj ${prop})
if (BUILD_SHARED_LIBS)
set_property(TARGET librnp-static PROPERTY ${prop} ${val})
- list(REMOVE_ITEM val "$<LINK_ONLY:sexp>")
+ list(REMOVE_ITEM val "$<LINK_ONLY:sexpp>")
set_property(TARGET librnp PROPERTY ${prop} ${val})
else()
set_property(TARGET librnp PROPERTY ${prop} ${val})
@@ -414,15 +435,14 @@ else()
endif()
# add these to the rnp-targets export
-# On Unix like systems we will build/install/pack shared and static libraries librnp.so and librnp.a
-# On Windows we will build/install/pack dynamic, import and static libraries rnp.dll, rnp.lib and rnp-static.lib
+# On Unix like systems we will build/install/pack either shared library librnp.so or static librnp.a
+# On Windows we will build/install/pack either dynamic and import libraries rnp.dll, rnp.lib or static library rnp-static.lib
-# If a client application uses shared rnp library, sexp is statically linked to librnp.so
-# If a client application uses static rnp library, it still needs libsexp.a
+# If a client application uses shared rnp library, sexpp is statically linked to librnp.so and libsexpp.a is not installed
+# If a client application uses static rnp library, it still needs libsexpp.a and it is installed
if (BUILD_SHARED_LIBS)
-# both static and shared libraries
-install(TARGETS librnp
+ install(TARGETS librnp
EXPORT rnp-targets
LIBRARY
DESTINATION "${CMAKE_INSTALL_LIBDIR}"
@@ -433,31 +453,34 @@ install(TARGETS librnp
COMPONENT development
)
- install(TARGETS librnp-static sexp
- EXPORT rnp-targets
- ARCHIVE
- DESTINATION "${CMAKE_INSTALL_LIBDIR}"
- COMPONENT development
- )
+# install dll only for windows
+ if (WIN32)
+ install(TARGETS librnp
+ RUNTIME
+ DESTINATION "${CMAKE_INSTALL_BINDIR}"
+ COMPONENT runtime
+ )
+ endif(WIN32)
else(BUILD_SHARED_LIBS)
-# static libraries only
-install(TARGETS librnp sexp
+# static libraries
+# install libsexpp unless system-installed libsexpp is used
+ if (SYSTEM_LIBSEXPP)
+ install(TARGETS librnp
+ EXPORT rnp-targets
+ ARCHIVE
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ COMPONENT development
+ )
+ else (SYSTEM_LIBSEXPP)
+ install(TARGETS librnp sexpp
EXPORT rnp-targets
ARCHIVE
DESTINATION "${CMAKE_INSTALL_LIBDIR}"
COMPONENT development
-)
+ )
+ endif (SYSTEM_LIBSEXPP)
endif(BUILD_SHARED_LIBS)
-# install dll only for windows
-if (WIN32)
- install(TARGETS librnp
- RUNTIME
- DESTINATION "${CMAKE_INSTALL_BINDIR}"
- COMPONENT runtime
- )
-endif(WIN32)
-
# install headers
install(
FILES
diff --git a/src/lib/config.h.in b/src/lib/config.h.in
index f8880d5..afb2cfe 100644
--- a/src/lib/config.h.in
+++ b/src/lib/config.h.in
@@ -51,8 +51,10 @@
#cmakedefine HAVE__TEMPNAM
#cmakedefine CRYPTO_BACKEND_BOTAN
+#cmakedefine CRYPTO_BACKEND_BOTAN3
#cmakedefine CRYPTO_BACKEND_OPENSSL
#cmakedefine CRYPTO_BACKEND_OPENSSL3
+#cmakedefine CRYPTO_BACKEND_OPENSSL3_LEGACY
#cmakedefine ENABLE_SM2
#cmakedefine ENABLE_AEAD
@@ -67,6 +69,6 @@
* we assume to be bundled with a sane implementation of std::regex. */
#if !defined(__GNUC__) || defined(_GLIBCXX_USE_CXX11_ABI) || \
(defined(WIN32) && !defined(MSYS)) || \
- ((defined(__clang__) && (__clang_major__ >= 4)) )
+ ((defined(__clang__) && (__clang_major__ >= 4)))
#define RNP_USE_STD_REGEX 1
#endif
diff --git a/src/lib/crypto.cpp b/src/lib/crypto.cpp
index 2634604..da3cba1 100644
--- a/src/lib/crypto.cpp
+++ b/src/lib/crypto.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2020, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023, [Ribose Inc](https://www.ribose.com).
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
@@ -128,7 +128,9 @@ pgp_generate_seckey(const rnp_keygen_crypto_params_t &crypto,
seckey.material.ec.curve = crypto.ecc.curve;
break;
}
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
case PGP_PKA_ECDSA:
case PGP_PKA_SM2:
if (!curve_supported(crypto.ecc.curve)) {
diff --git a/src/lib/crypto/backend_version.cpp b/src/lib/crypto/backend_version.cpp
index 859b048..32af839 100644
--- a/src/lib/crypto/backend_version.cpp
+++ b/src/lib/crypto/backend_version.cpp
@@ -72,7 +72,7 @@ backend_version()
if (version[0]) {
return version;
}
- const char *reg = "OpenSSL (([0-9]\\.[0-9]\\.[0-9])[a-z]*(-beta[0-9])*(-dev)*) ";
+ const char *reg = "OpenSSL (([0-9]+\\.[0-9]+\\.[0-9]+)[a-z]*(-[a-z0-9]+)*) ";
#ifndef RNP_USE_STD_REGEX
static regex_t r;
regmatch_t matches[5];
@@ -84,7 +84,9 @@ backend_version()
return "unknown";
}
}
- if (regexec(&r, ver, 5, matches, 0) != 0) {
+ int res = regexec(&r, ver, 5, matches, 0);
+ if (res != 0) {
+ RNP_LOG("regexec() failed on %s: %d", ver, res);
return "unknown";
}
assert(sizeof(version) > matches[1].rm_eo - matches[1].rm_so);
@@ -95,6 +97,7 @@ backend_version()
std::smatch result;
std::string ver = OpenSSL_version(OPENSSL_VERSION);
if (!std::regex_search(ver, result, re)) {
+ RNP_LOG("std::regex_search failed on \"%s\"", ver.c_str());
return "unknown";
}
assert(sizeof(version) > result[1].str().size());
@@ -109,7 +112,10 @@ backend_version()
#if defined(CRYPTO_BACKEND_OPENSSL3)
#if defined(ENABLE_IDEA) || defined(ENABLE_CAST5) || defined(ENABLE_BLOWFISH) || \
- defined(ENABLE_RIPEMD160)
+ (defined(ENABLE_RIPEMD160) && OPENSSL_VERSION_NUMBER < 0x30000070L)
+#if !defined(CRYPTO_BACKEND_OPENSSL3_LEGACY)
+#error "OpenSSL doesn't have legacy provider, however one of the features enables it's load."
+#endif
#define OPENSSL_LOAD_LEGACY
#endif
diff --git a/src/lib/crypto/bn.h b/src/lib/crypto/bn.h
index 26cc547..a4cfa1a 100644
--- a/src/lib/crypto/bn.h
+++ b/src/lib/crypto/bn.h
@@ -61,4 +61,82 @@ bool bn2mpi(const bignum_t *bn, pgp_mpi_t *val);
size_t bn_num_bytes(const bignum_t &a);
+#if defined(CRYPTO_BACKEND_OPENSSL)
+namespace rnp {
+class bn {
+ BIGNUM *_bn;
+
+ public:
+ bn(BIGNUM *val = NULL) : _bn(val)
+ {
+ }
+
+ bn(const pgp_mpi_t &val) : _bn(mpi2bn(&val))
+ {
+ }
+
+ ~bn()
+ {
+ BN_free(_bn);
+ }
+
+ void
+ set(BIGNUM *val = NULL) noexcept
+ {
+ BN_free(_bn);
+ _bn = val;
+ }
+
+ void
+ set(const pgp_mpi_t &val) noexcept
+ {
+ BN_free(_bn);
+ _bn = mpi2bn(&val);
+ }
+
+ BIGNUM **
+ ptr() noexcept
+ {
+ set();
+ return &_bn;
+ }
+
+ BIGNUM *
+ get() noexcept
+ {
+ return _bn;
+ }
+
+ BIGNUM *
+ own() noexcept
+ {
+ auto res = _bn;
+ _bn = NULL;
+ return res;
+ }
+
+ size_t
+ bytes() const noexcept
+ {
+ return BN_num_bytes(_bn);
+ }
+
+ bool
+ bin(uint8_t *b) const noexcept
+ {
+ if (!b) {
+ return false;
+ }
+ return BN_bn2bin(_bn, b) >= 0;
+ }
+
+ bool
+ mpi(pgp_mpi_t &mpi) const noexcept
+ {
+ return bn2mpi(_bn, &mpi);
+ }
+};
+}; // namespace rnp
+#endif
+
#endif
diff --git a/src/lib/crypto/cipher.hpp b/src/lib/crypto/cipher.hpp
index c9edf15..f683b76 100644
--- a/src/lib/crypto/cipher.hpp
+++ b/src/lib/crypto/cipher.hpp
@@ -58,7 +58,10 @@ class Cipher {
const uint8_t *input,
size_t input_length,
size_t * input_consumed) = 0;
- // process final block and perform any padding
+ /**
+ * @brief Finalize cipher. For AEAD mode, depending on backend, may require whole
+ * authentication tag to be present in input.
+ */
virtual bool finish(uint8_t * output,
size_t output_length,
size_t * output_written,
diff --git a/src/lib/crypto/cipher_botan.cpp b/src/lib/crypto/cipher_botan.cpp
index c2c4ab3..6f4a4fc 100644
--- a/src/lib/crypto/cipher_botan.cpp
+++ b/src/lib/crypto/cipher_botan.cpp
@@ -64,8 +64,12 @@ Cipher_Botan::create(pgp_symm_alg_t alg, const std::string &name, bool encrypt)
return nullptr;
}
#endif
- auto cipher = Botan::Cipher_Mode::create(
- name, encrypt ? Botan::Cipher_Dir::ENCRYPTION : Botan::Cipher_Dir::DECRYPTION);
+#if defined(CRYPTO_BACKEND_BOTAN3)
+ auto dir = encrypt ? Botan::Cipher_Dir::Encryption : Botan::Cipher_Dir::Decryption;
+#else
+ auto dir = encrypt ? Botan::Cipher_Dir::ENCRYPTION : Botan::Cipher_Dir::DECRYPTION;
+#endif
+ auto cipher = Botan::Cipher_Mode::create(name, dir);
if (!cipher) {
RNP_LOG("Failed to create cipher '%s'", name.c_str());
return nullptr;
diff --git a/src/lib/crypto/dl_ossl.cpp b/src/lib/crypto/dl_ossl.cpp
index 1e96218..4845baa 100644
--- a/src/lib/crypto/dl_ossl.cpp
+++ b/src/lib/crypto/dl_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021, 2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -33,6 +33,34 @@
#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/evp.h>
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#endif
+
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+static OSSL_PARAM *
+dl_build_params(bignum_t *p, bignum_t *q, bignum_t *g, bignum_t *y, bignum_t *x)
+{
+ OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
+ if (!bld) {
+ return NULL; // LCOV_EXCL_LINE
+ }
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) ||
+ (q && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q)) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, y) ||
+ (x && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, x))) {
+ /* LCOV_EXCL_START */
+ OSSL_PARAM_BLD_free(bld);
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ OSSL_PARAM *param = OSSL_PARAM_BLD_to_param(bld);
+ OSSL_PARAM_BLD_free(bld);
+ return param;
+}
+#endif
EVP_PKEY *
dl_load_key(const pgp_mpi_t &mp,
@@ -41,63 +69,89 @@ dl_load_key(const pgp_mpi_t &mp,
const pgp_mpi_t &my,
const pgp_mpi_t *mx)
{
- DH * dh = NULL;
EVP_PKEY *evpkey = NULL;
- bignum_t *p = mpi2bn(&mp);
- bignum_t *q = mq ? mpi2bn(mq) : NULL;
- bignum_t *g = mpi2bn(&mg);
- bignum_t *y = mpi2bn(&my);
- bignum_t *x = mx ? mpi2bn(mx) : NULL;
+ rnp::bn p(mpi2bn(&mp));
+ rnp::bn q(mq ? mpi2bn(mq) : NULL);
+ rnp::bn g(mpi2bn(&mg));
+ rnp::bn y(mpi2bn(&my));
+ rnp::bn x(mx ? mpi2bn(mx) : NULL);
- if (!p || (mq && !q) || !g || !y || (mx && !x)) {
+ if (!p.get() || (mq && !q.get()) || !g.get() || !y.get() || (mx && !x.get())) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- dh = DH_new();
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ OSSL_PARAM *params = dl_build_params(p.get(), q.get(), g.get(), y.get(), x.get());
+ if (!params) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to build dsa params");
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DH, NULL);
+ if (!ctx) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to create dl context");
+ OSSL_PARAM_free(params);
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ if ((EVP_PKEY_fromdata_init(ctx) != 1) ||
+ (EVP_PKEY_fromdata(
+ ctx, &evpkey, mx ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY, params) != 1)) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to create key from data");
+ evpkey = NULL;
+ /* LCOV_EXCL_END */
+ }
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+ return evpkey;
+#else
+ DH *dh = DH_new();
if (!dh) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- int res;
/* line below must not fail */
- res = DH_set0_pqg(dh, p, q, g);
+ int res = DH_set0_pqg(dh, p.own(), q.own(), g.own());
assert(res == 1);
if (res < 1) {
goto done;
}
- p = NULL;
- q = NULL;
- g = NULL;
/* line below must not fail */
- res = DH_set0_key(dh, y, x);
+ res = DH_set0_key(dh, y.own(), x.own());
assert(res == 1);
if (res < 1) {
goto done;
}
- y = NULL;
- x = NULL;
evpkey = EVP_PKEY_new();
if (!evpkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("allocation failed");
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_set1_DH(evpkey, dh) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set key: %lu", ERR_peek_last_error());
EVP_PKEY_free(evpkey);
evpkey = NULL;
+ /* LCOV_EXCL_END */
}
done:
DH_free(dh);
- bn_free(p);
- bn_free(q);
- bn_free(g);
- bn_free(y);
- bn_free(x);
return evpkey;
+#endif
}
+#if !defined(CRYPTO_BACKEND_OPENSSL3)
static rnp_result_t
dl_validate_secret_key(EVP_PKEY *dlkey, const pgp_mpi_t &mx)
{
@@ -117,22 +171,28 @@ dl_validate_secret_key(EVP_PKEY *dlkey, const pgp_mpi_t &mx)
bignum_t *cy = bn_new();
if (!x || !cy || !ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed");
goto done;
+ /* LCOV_EXCL_END */
}
if (!q) {
/* if q is NULL then group order is (p - 1) / 2 */
p1 = BN_dup(p);
if (!p1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed");
goto done;
+ /* LCOV_EXCL_END */
}
int res;
res = BN_rshift(p1, p1, 1);
assert(res == 1);
if (res < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("BN_rshift failed.");
goto done;
+ /* LCOV_EXCL_END */
}
q = p1;
}
@@ -154,6 +214,7 @@ done:
bn_free(p1);
return ret;
}
+#endif
rnp_result_t
dl_validate_key(EVP_PKEY *pkey, const pgp_mpi_t *x)
@@ -161,8 +222,10 @@ dl_validate_key(EVP_PKEY *pkey, const pgp_mpi_t *x)
rnp_result_t ret = RNP_ERROR_GENERIC;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
int res;
res = EVP_PKEY_param_check(ctx);
@@ -181,6 +244,12 @@ dl_validate_key(EVP_PKEY *pkey, const pgp_mpi_t *x)
goto done;
}
}
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ res = x ? EVP_PKEY_pairwise_check(ctx) : EVP_PKEY_public_check(ctx);
+ if (res == 1) {
+ ret = RNP_SUCCESS;
+ }
+#else
res = EVP_PKEY_public_check(ctx);
if (res < 0) {
RNP_LOG("Key validation error: %lu", ERR_peek_last_error());
@@ -194,6 +263,7 @@ dl_validate_key(EVP_PKEY *pkey, const pgp_mpi_t *x)
goto done;
}
ret = dl_validate_secret_key(pkey, *x);
+#endif
done:
EVP_PKEY_CTX_free(ctx);
return ret;
diff --git a/src/lib/crypto/dsa_ossl.cpp b/src/lib/crypto/dsa_ossl.cpp
index 1fb75b5..3f91b72 100644
--- a/src/lib/crypto/dsa_ossl.cpp
+++ b/src/lib/crypto/dsa_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021, 2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -35,6 +35,10 @@
#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/evp.h>
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#endif
#define DSA_MAX_Q_BITLEN 256
@@ -83,66 +87,118 @@ done:
return res;
}
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+static OSSL_PARAM *
+dsa_build_params(bignum_t *p, bignum_t *q, bignum_t *g, bignum_t *y, bignum_t *x)
+{
+ OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
+ if (!bld) {
+ return NULL; // LCOV_EXCL_LINE
+ }
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, y) ||
+ (x && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, x))) {
+ /* LCOV_EXCL_START */
+ OSSL_PARAM_BLD_free(bld);
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ OSSL_PARAM *param = OSSL_PARAM_BLD_to_param(bld);
+ OSSL_PARAM_BLD_free(bld);
+ return param;
+}
+#endif
+
static EVP_PKEY *
dsa_load_key(const pgp_dsa_key_t *key, bool secret = false)
{
- DSA * dsa = NULL;
EVP_PKEY *evpkey = NULL;
- bignum_t *p = mpi2bn(&key->p);
- bignum_t *q = mpi2bn(&key->q);
- bignum_t *g = mpi2bn(&key->g);
- bignum_t *y = mpi2bn(&key->y);
- bignum_t *x = secret ? mpi2bn(&key->x) : NULL;
+ rnp::bn p(mpi2bn(&key->p));
+ rnp::bn q(mpi2bn(&key->q));
+ rnp::bn g(mpi2bn(&key->g));
+ rnp::bn y(mpi2bn(&key->y));
+ rnp::bn x(secret ? mpi2bn(&key->x) : NULL);
- if (!p || !q || !g || !y || (secret && !x)) {
+ if (!p.get() || !q.get() || !g.get() || !y.get() || (secret && !x.get())) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- dsa = DSA_new();
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ OSSL_PARAM *params = dsa_build_params(p.get(), q.get(), g.get(), y.get(), x.get());
+ if (!params) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to build dsa params");
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL);
+ if (!ctx) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to create dsa context");
+ OSSL_PARAM_free(params);
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ if ((EVP_PKEY_fromdata_init(ctx) != 1) ||
+ (EVP_PKEY_fromdata(
+ ctx, &evpkey, secret ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY, params) != 1)) {
+ RNP_LOG("failed to create key from data");
+ evpkey = NULL;
+ }
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+ return evpkey;
+#else
+ DSA *dsa = DSA_new();
if (!dsa) {
+ /* LCOV_EXCL_START */
RNP_LOG("Out of memory");
goto done;
+ /* LCOV_EXCL_END */
}
- if (DSA_set0_pqg(dsa, p, q, g) != 1) {
+ if (DSA_set0_pqg(dsa, p.own(), q.own(), g.own()) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set pqg. Error: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
- p = NULL;
- q = NULL;
- g = NULL;
- if (DSA_set0_key(dsa, y, x) != 1) {
+ if (DSA_set0_key(dsa, y.own(), x.own()) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Secret key load error: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
- y = NULL;
- x = NULL;
evpkey = EVP_PKEY_new();
if (!evpkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("allocation failed");
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_set1_DSA(evpkey, dsa) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set key: %lu", ERR_peek_last_error());
EVP_PKEY_free(evpkey);
evpkey = NULL;
+ /* LCOV_EXCL_END */
}
done:
DSA_free(dsa);
- bn_free(p);
- bn_free(q);
- bn_free(g);
- bn_free(y);
- bn_free(x);
return evpkey;
+#endif
}
rnp_result_t
dsa_validate_key(rnp::RNG *rng, const pgp_dsa_key_t *key, bool secret)
{
/* OpenSSL doesn't implement key checks for the DSA, however we may use DL via DH */
- EVP_PKEY *pkey = dl_load_key(key->p, &key->q, key->g, key->y, NULL);
+ EVP_PKEY *pkey = dl_load_key(key->p, &key->q, key->g, key->y, secret ? &key->x : NULL);
if (!pkey) {
RNP_LOG("Failed to load key");
return RNP_ERROR_BAD_PARAMETERS;
@@ -175,8 +231,10 @@ dsa_sign(rnp::RNG * rng,
/* init context and sign */
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_sign_init(ctx) <= 0) {
RNP_LOG("Failed to initialize signing: %lu", ERR_peek_last_error());
@@ -216,8 +274,10 @@ dsa_verify(const pgp_dsa_signature_t *sig,
/* init context and sign */
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_verify_init(ctx) <= 0) {
RNP_LOG("Failed to initialize verify: %lu", ERR_peek_last_error());
@@ -238,6 +298,43 @@ done:
return ret;
}
+static bool
+dsa_extract_key(EVP_PKEY *pkey, pgp_dsa_key_t &key)
+{
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ rnp::bn p;
+ rnp::bn q;
+ rnp::bn g;
+ rnp::bn y;
+ rnp::bn x;
+
+ bool res = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_P, p.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_Q, q.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_G, g.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, y.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, x.ptr());
+ return res && p.mpi(key.p) && q.mpi(key.q) && g.mpi(key.g) && y.mpi(key.y) && x.mpi(key.x);
+#else
+ const DSA *dsa = EVP_PKEY_get0_DSA(pkey);
+ if (!dsa) {
+ RNP_LOG("Failed to retrieve DSA key: %lu", ERR_peek_last_error());
+ return false;
+ }
+
+ const bignum_t *p = DSA_get0_p(dsa);
+ const bignum_t *q = DSA_get0_q(dsa);
+ const bignum_t *g = DSA_get0_g(dsa);
+ const bignum_t *y = DSA_get0_pub_key(dsa);
+ const bignum_t *x = DSA_get0_priv_key(dsa);
+
+ if (!p || !q || !g || !y || !x) {
+ return false;
+ }
+ return bn2mpi(p, &key.p) && bn2mpi(q, &key.q) && bn2mpi(g, &key.g) && bn2mpi(y, &key.y) &&
+ bn2mpi(x, &key.x);
+#endif
+}
+
rnp_result_t
dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits)
{
@@ -246,7 +343,6 @@ dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits)
}
rnp_result_t ret = RNP_ERROR_GENERIC;
- const DSA * dsa = NULL;
EVP_PKEY * pkey = NULL;
EVP_PKEY * parmkey = NULL;
EVP_PKEY_CTX *ctx = NULL;
@@ -254,12 +350,16 @@ dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits)
/* Generate DSA params */
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
return ret;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_paramgen_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx, keylen) <= 0) {
RNP_LOG("Failed to set key bits: %lu", ERR_peek_last_error());
@@ -293,32 +393,10 @@ dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits)
RNP_LOG("DSA keygen failed: %lu", ERR_peek_last_error());
goto done;
}
- dsa = EVP_PKEY_get0_DSA(pkey);
- if (!dsa) {
- RNP_LOG("Failed to retrieve DSA key: %lu", ERR_peek_last_error());
- goto done;
- }
- const bignum_t *p;
- const bignum_t *q;
- const bignum_t *g;
- const bignum_t *y;
- const bignum_t *x;
- p = DSA_get0_p(dsa);
- q = DSA_get0_q(dsa);
- g = DSA_get0_g(dsa);
- y = DSA_get0_pub_key(dsa);
- x = DSA_get0_priv_key(dsa);
- if (!p || !q || !g || !y || !x) {
- ret = RNP_ERROR_BAD_STATE;
- goto done;
+ if (dsa_extract_key(pkey, *key)) {
+ ret = RNP_SUCCESS;
}
- bn2mpi(p, &key->p);
- bn2mpi(q, &key->q);
- bn2mpi(g, &key->g);
- bn2mpi(y, &key->y);
- bn2mpi(x, &key->x);
- ret = RNP_SUCCESS;
done:
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(parmkey);
diff --git a/src/lib/crypto/ec_ossl.cpp b/src/lib/crypto/ec_ossl.cpp
index 6974b4c..46c4677 100644
--- a/src/lib/crypto/ec_ossl.cpp
+++ b/src/lib/crypto/ec_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021, 2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -36,6 +36,10 @@
#include <openssl/objects.h>
#include <openssl/err.h>
#include <openssl/ec.h>
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#endif
static bool
ec_is_raw_key(const pgp_curve_t curve)
@@ -61,26 +65,34 @@ ec_generate_pkey(const pgp_pubkey_alg_t alg_id, const pgp_curve_t curve)
}
int nid = OBJ_sn2nid(ec_desc->openssl_name);
if (nid == NID_undef) {
+ /* LCOV_EXCL_START */
RNP_LOG("Unknown SN: %s", ec_desc->openssl_name);
return NULL;
+ /* LCOV_EXCL_END */
}
bool raw = ec_is_raw_key(curve);
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(raw ? nid : EVP_PKEY_EC, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
return NULL;
+ /* LCOV_EXCL_END */
}
EVP_PKEY *pkey = NULL;
if (EVP_PKEY_keygen_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (!raw && (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) <= 0)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set curve nid: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
- RNP_LOG("EC keygen failed: %lu", ERR_peek_last_error());
+ RNP_LOG("EC keygen failed: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
}
done:
EVP_PKEY_CTX_free(ctx);
@@ -94,8 +106,10 @@ ec_write_raw_seckey(EVP_PKEY *pkey, pgp_ec_key_t *key)
static_assert(sizeof(key->x.mpi) > 32, "mpi is too small.");
key->x.len = sizeof(key->x.mpi);
if (EVP_PKEY_get_raw_private_key(pkey, key->x.mpi, &key->x.len) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed get raw private key: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
assert(key->x.len == 32);
if (EVP_PKEY_id(pkey) == EVP_PKEY_X25519) {
@@ -107,6 +121,30 @@ ec_write_raw_seckey(EVP_PKEY *pkey, pgp_ec_key_t *key)
return true;
}
+static bool
+ec_write_seckey(EVP_PKEY *pkey, pgp_mpi_t &key)
+{
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ rnp::bn x;
+ return EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, x.ptr()) &&
+ bn2mpi(x.get(), &key);
+#else
+ const bignum_t *x = NULL;
+ const EC_KEY * ec = EVP_PKEY_get0_EC_KEY(pkey);
+ if (!ec) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("Failed to retrieve EC key: %lu", ERR_peek_last_error());
+ return false;
+ /* LCOV_EXCL_END */
+ }
+ x = EC_KEY_get0_private_key(ec);
+ if (!x) {
+ return false;
+ }
+ return bn2mpi(x, &key);
+#endif
+}
+
rnp_result_t
ec_generate(rnp::RNG * rng,
pgp_ec_key_t * key,
@@ -125,24 +163,19 @@ ec_generate(rnp::RNG * rng,
EVP_PKEY_free(pkey);
return ret;
}
- const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
- if (!ec) {
- RNP_LOG("Failed to retrieve EC key: %lu", ERR_peek_last_error());
- goto done;
- }
if (!ec_write_pubkey(pkey, key->p, curve)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to write pubkey.");
goto done;
+ /* LCOV_EXCL_END */
}
- const bignum_t *x;
- x = EC_KEY_get0_private_key(ec);
- if (!x) {
- ret = RNP_ERROR_BAD_STATE;
+ if (!ec_write_seckey(pkey, key->x)) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("Failed to write seckey.");
goto done;
+ /* LCOV_EXCL_END */
}
- if (bn2mpi(x, &key->x)) {
- ret = RNP_SUCCESS;
- }
+ ret = RNP_SUCCESS;
done:
EVP_PKEY_free(pkey);
return ret;
@@ -161,7 +194,7 @@ ec_load_raw_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, int nid)
EVP_PKEY *evpkey =
EVP_PKEY_new_raw_public_key(nid, NULL, &keyp.mpi[1], mpi_bytes(&keyp) - 1);
if (!evpkey) {
- RNP_LOG("Failed to load public key: %lu", ERR_peek_last_error());
+ RNP_LOG("Failed to load public key: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
}
return evpkey;
}
@@ -189,10 +222,68 @@ ec_load_raw_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, int nid)
evpkey = EVP_PKEY_new_raw_private_key(nid, NULL, prkey.data(), 32);
}
if (!evpkey) {
- RNP_LOG("Failed to load private key: %lu", ERR_peek_last_error());
+ RNP_LOG("Failed to load private key: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
+ }
+ return evpkey;
+}
+
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+static OSSL_PARAM *
+ec_build_params(const pgp_mpi_t &p, bignum_t *x, const char *curve)
+{
+ OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
+ if (!bld) {
+ return NULL;
+ }
+ if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, curve, 0) ||
+ !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, p.mpi, p.len) ||
+ (x && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, x))) {
+ /* LCOV_EXCL_START */
+ OSSL_PARAM_BLD_free(bld);
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ OSSL_PARAM *param = OSSL_PARAM_BLD_to_param(bld);
+ OSSL_PARAM_BLD_free(bld);
+ return param;
+}
+
+static EVP_PKEY *
+ec_load_key_openssl3(const pgp_mpi_t & keyp,
+ const pgp_mpi_t * keyx,
+ const ec_curve_desc_t *curv_desc)
+{
+ rnp::bn x(keyx ? mpi2bn(keyx) : NULL);
+ OSSL_PARAM *params = ec_build_params(keyp, x.get(), curv_desc->openssl_name);
+ if (!params) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to build ec params");
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
+ if (!ctx) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to create ec context");
+ OSSL_PARAM_free(params);
+ return NULL;
+ /* LCOV_EXCL_END */
}
+ EVP_PKEY *evpkey = NULL;
+ if ((EVP_PKEY_fromdata_init(ctx) != 1) ||
+ (EVP_PKEY_fromdata(
+ ctx, &evpkey, keyx ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY, params) != 1)) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to create ec key from data");
+ /* Some version of OpenSSL may leave evpkey non-NULL after failure, so let's be safe */
+ evpkey = NULL;
+ /* LCOV_EXCL_END */
+ }
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
return evpkey;
}
+#endif
EVP_PKEY *
ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve)
@@ -208,20 +299,27 @@ ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve)
}
int nid = OBJ_sn2nid(curv_desc->openssl_name);
if (nid == NID_undef) {
+ /* LCOV_EXCL_START */
RNP_LOG("Unknown SN: %s", curv_desc->openssl_name);
return NULL;
+ /* LCOV_EXCL_END */
}
/* EdDSA and X25519 keys are loaded in a different way */
if (ec_is_raw_key(curve)) {
return ec_load_raw_key(keyp, keyx, nid);
}
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ return ec_load_key_openssl3(keyp, keyx, curv_desc);
+#else
EC_KEY *ec = EC_KEY_new_by_curve_name(nid);
if (!ec) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create EC key with group %d (%s): %s",
nid,
curv_desc->openssl_name,
ERR_reason_error_string(ERR_peek_last_error()));
return NULL;
+ /* LCOV_EXCL_END */
}
bool res = false;
@@ -229,22 +327,30 @@ ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve)
EVP_PKEY *pkey = NULL;
EC_POINT *p = EC_POINT_new(EC_KEY_get0_group(ec));
if (!p) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to allocate point: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EC_POINT_oct2point(EC_KEY_get0_group(ec), p, keyp.mpi, keyp.len, NULL) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to decode point: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EC_KEY_set_public_key(ec, p) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set public key: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
pkey = EVP_PKEY_new();
if (!pkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("EVP_PKEY allocation failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (!keyx) {
res = true;
@@ -253,12 +359,16 @@ ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve)
x = mpi2bn(keyx);
if (!x) {
+ /* LCOV_EXCL_START */
RNP_LOG("allocation failed");
goto done;
+ /* LCOV_EXCL_END */
}
if (EC_KEY_set_private_key(ec, x) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set secret key: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
res = true;
done:
@@ -273,6 +383,7 @@ done:
pkey = NULL;
}
return pkey;
+#endif
}
rnp_result_t
@@ -296,14 +407,18 @@ ec_validate_key(const pgp_ec_key_t &key, bool secret)
rnp_result_t ret = RNP_ERROR_GENERIC;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
int res;
res = secret ? EVP_PKEY_check(ctx) : EVP_PKEY_public_check(ctx);
if (res < 0) {
+ /* LCOV_EXCL_START */
auto err = ERR_peek_last_error();
RNP_LOG("EC key check failed: %lu (%s)", err, ERR_reason_error_string(err));
+ /* LCOV_EXCL_END */
}
if (res > 0) {
ret = RNP_SUCCESS;
@@ -321,29 +436,61 @@ ec_write_pubkey(EVP_PKEY *pkey, pgp_mpi_t &mpi, pgp_curve_t curve)
/* EdDSA and X25519 keys are saved in a different way */
mpi.len = sizeof(mpi.mpi) - 1;
if (EVP_PKEY_get_raw_public_key(pkey, &mpi.mpi[1], &mpi.len) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed get raw public key: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
assert(mpi.len == 32);
mpi.mpi[0] = 0x40;
mpi.len++;
return true;
}
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ const ec_curve_desc_t *ec_desc = get_curve_desc(curve);
+ if (!ec_desc) {
+ return false;
+ }
+ size_t flen = BITS_TO_BYTES(ec_desc->bitlen);
+ rnp::bn qx;
+ rnp::bn qy;
+
+ /* OpenSSL before 3.0.9 by default uses compressed point for OSSL_PKEY_PARAM_PUB_KEY so use
+ * this approach */
+ bool res = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, qx.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, qy.ptr());
+ if (!res) {
+ return false;
+ }
+ /* Compose uncompressed point in mpi */
+ size_t xlen = qx.bytes();
+ size_t ylen = qy.bytes();
+ assert((xlen <= flen) && (ylen <= flen));
+ memset(mpi.mpi, 0, sizeof(mpi.mpi));
+ mpi.mpi[0] = 0x04;
+ mpi.len = 2 * flen + 1;
+ return qx.bin(&mpi.mpi[1 + flen - xlen]) && qy.bin(&mpi.mpi[1 + 2 * flen - ylen]);
+#else
const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
if (!ec) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to retrieve EC key: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
const EC_POINT *p = EC_KEY_get0_public_key(ec);
if (!p) {
+ /* LCOV_EXCL_START */
RNP_LOG("Null point: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
/* call below adds leading zeroes if needed */
mpi.len = EC_POINT_point2oct(
EC_KEY_get0_group(ec), p, POINT_CONVERSION_UNCOMPRESSED, mpi.mpi, sizeof(mpi.mpi), NULL);
if (!mpi.len) {
- RNP_LOG("Failed to encode public key: %lu", ERR_peek_last_error());
+ RNP_LOG("Failed to encode public key: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
}
return mpi.len;
+#endif
}
diff --git a/src/lib/crypto/ecdh.cpp b/src/lib/crypto/ecdh.cpp
index d4411c3..574b3b8 100644
--- a/src/lib/crypto/ecdh.cpp
+++ b/src/lib/crypto/ecdh.cpp
@@ -261,7 +261,13 @@ ecdh_encrypt_pkcs5(rnp::RNG * rng,
}
out->mlen = sizeof(out->m);
+#if defined(CRYPTO_BACKEND_BOTAN3)
+ char name[8];
+ snprintf(name, sizeof(name), "AES-%zu", 8 * kek_len);
+ if (botan_nist_kw_enc(name, 0, m, m_padded_len, kek, kek_len, out->m, &out->mlen)) {
+#else
if (botan_key_wrap3394(m, m_padded_len, kek, kek_len, out->m, &out->mlen)) {
+#endif
goto end;
}
@@ -354,8 +360,15 @@ ecdh_decrypt_pkcs5(uint8_t * out,
goto end;
}
+#if defined(CRYPTO_BACKEND_BOTAN3)
+ char name[8];
+ snprintf(name, sizeof(name), "AES-%zu", 8 * kek_len);
+ if (botan_nist_kw_dec(
+ name, 0, in->m, in->mlen, kek.data(), kek_len, deckey.data(), &deckey_len)) {
+#else
if (botan_key_unwrap3394(
in->m, in->mlen, kek.data(), kek_len, deckey.data(), &deckey_len)) {
+#endif
goto end;
}
diff --git a/src/lib/crypto/ecdh_ossl.cpp b/src/lib/crypto/ecdh_ossl.cpp
index 60b7260..5660318 100644
--- a/src/lib/crypto/ecdh_ossl.cpp
+++ b/src/lib/crypto/ecdh_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021-2023, [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -70,8 +70,10 @@ ecdh_derive_kek(uint8_t * x,
const size_t hash_len = rnp::Hash::size(key.kdf_hash_alg);
if (!hash_len) {
// must not assert here as kdf/hash algs are not checked during key parsing
+ /* LCOV_EXCL_START */
RNP_LOG("Unsupported key wrap hash algorithm.");
return RNP_ERROR_NOT_SUPPORTED;
+ /* LCOV_EXCL_END */
}
size_t other_len = kdf_other_info_serialize(
other_info, curve_desc, fingerprint, key.kdf_hash_alg, key.key_wrap_alg);
@@ -83,8 +85,10 @@ ecdh_derive_kek(uint8_t * x,
size_t reps = (kek_len + hash_len - 1) / hash_len;
// As we use AES & SHA2 we should not get more then 2 iterations
if (reps > 2) {
+ /* LCOV_EXCL_START */
RNP_LOG("Invalid key wrap/hash alg combination.");
return RNP_ERROR_NOT_SUPPORTED;
+ /* LCOV_EXCL_END */
}
size_t have = 0;
try {
@@ -100,8 +104,10 @@ ecdh_derive_kek(uint8_t * x,
}
return RNP_SUCCESS;
} catch (const std::exception &e) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to derive kek: %s", e.what());
return RNP_ERROR_GENERIC;
+ /* LCOV_EXCL_END */
}
}
@@ -115,27 +121,35 @@ ecdh_rfc3394_wrap_ctx(EVP_CIPHER_CTX **ctx,
const char *cipher_name = NULL;
ARRAY_LOOKUP_BY_ID(ecdh_wrap_alg_map, alg, name, wrap_alg, cipher_name);
if (!cipher_name) {
+ /* LCOV_EXCL_START */
RNP_LOG("Unsupported key wrap algorithm: %d", (int) wrap_alg);
return RNP_ERROR_NOT_SUPPORTED;
+ /* LCOV_EXCL_END */
}
const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_name);
if (!cipher) {
+ /* LCOV_EXCL_START */
RNP_LOG("Cipher %s is not supported by OpenSSL.", cipher_name);
return RNP_ERROR_NOT_SUPPORTED;
+ /* LCOV_EXCL_END */
}
*ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed : %lu", ERR_peek_last_error());
return RNP_ERROR_OUT_OF_MEMORY;
+ /* LCOV_EXCL_END */
}
EVP_CIPHER_CTX_set_flags(*ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
int res = decrypt ? EVP_DecryptInit_ex(*ctx, cipher, NULL, key, NULL) :
EVP_EncryptInit_ex(*ctx, cipher, NULL, key, NULL);
if (res <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to initialize cipher : %lu", ERR_peek_last_error());
EVP_CIPHER_CTX_free(*ctx);
*ctx = NULL;
return RNP_ERROR_GENERIC;
+ /* LCOV_EXCL_END */
}
return RNP_SUCCESS;
}
@@ -151,14 +165,16 @@ ecdh_rfc3394_wrap(uint8_t * out,
EVP_CIPHER_CTX *ctx = NULL;
rnp_result_t ret = ecdh_rfc3394_wrap_ctx(&ctx, wrap_alg, key, false);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Wrap context initialization failed.");
return ret;
+ /* LCOV_EXCL_END */
}
int intlen = *out_len;
/* encrypts in one pass, no final is needed */
int res = EVP_EncryptUpdate(ctx, out, &intlen, in, in_len);
if (res <= 0) {
- RNP_LOG("Failed to encrypt data : %lu", ERR_peek_last_error());
+ RNP_LOG("Failed to encrypt data : %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
} else {
*out_len = intlen;
}
@@ -181,8 +197,10 @@ ecdh_rfc3394_unwrap(uint8_t * out,
EVP_CIPHER_CTX *ctx = NULL;
rnp_result_t ret = ecdh_rfc3394_wrap_ctx(&ctx, wrap_alg, key, true);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Unwrap context initialization failed.");
return ret;
+ /* LCOV_EXCL_END */
}
int intlen = *out_len;
/* decrypts in one pass, no final is needed */
@@ -201,21 +219,29 @@ ecdh_derive_secret(EVP_PKEY *sec, EVP_PKEY *peer, uint8_t *x, size_t *xlen)
{
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(sec, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
bool res = false;
if (EVP_PKEY_derive_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Key derivation init failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_derive_set_peer(ctx, peer) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Peer setting failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_derive(ctx, x, xlen) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to obtain shared secret size: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
res = true;
done:
@@ -256,14 +282,18 @@ ecdh_encrypt_pkcs5(rnp::RNG * rng,
/* check whether we have valid wrap_alg before doing heavy operations */
size_t keklen = ecdh_kek_len(key->key_wrap_alg);
if (!keklen) {
+ /* LCOV_EXCL_START */
RNP_LOG("Unsupported key wrap algorithm: %d", (int) key->key_wrap_alg);
return RNP_ERROR_NOT_SUPPORTED;
+ /* LCOV_EXCL_END */
}
/* load our public key */
EVP_PKEY *pkey = ec_load_key(key->p, NULL, key->curve);
if (!pkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to load public key.");
return RNP_ERROR_BAD_PARAMETERS;
+ /* LCOV_EXCL_END */
}
rnp::secure_array<uint8_t, MAX_CURVE_BYTELEN + 1> sec;
rnp::secure_array<uint8_t, MAX_AES_KEY_SIZE> kek;
@@ -274,28 +304,36 @@ ecdh_encrypt_pkcs5(rnp::RNG * rng,
/* generate ephemeral key */
EVP_PKEY *ephkey = ec_generate_pkey(PGP_PKA_ECDH, key->curve);
if (!ephkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to generate ephemeral key.");
ret = RNP_ERROR_KEY_GENERATION;
goto done;
+ /* LCOV_EXCL_END */
}
/* do ECDH derivation */
if (!ecdh_derive_secret(ephkey, pkey, sec.data(), &seclen)) {
+ /* LCOV_EXCL_START */
RNP_LOG("ECDH derivation failed.");
goto done;
+ /* LCOV_EXCL_END */
}
/* here we got x value in sec, deriving kek */
ret = ecdh_derive_kek(sec.data(), seclen, *key, fingerprint, kek.data(), keklen);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to derive KEK.");
goto done;
+ /* LCOV_EXCL_END */
}
/* add PKCS#7 padding */
size_t m_padded_len;
m_padded_len = ((in_len / 8) + 1) * 8;
memcpy(mpad.data(), in, in_len);
if (!pad_pkcs7(mpad.data(), m_padded_len, in_len)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to add PKCS #7 padding.");
goto done;
+ /* LCOV_EXCL_END */
}
/* do RFC 3394 AES key wrap */
static_assert(sizeof(out->m) == ECDH_WRAPPED_KEY_SIZE, "Wrong ECDH wrapped key size.");
@@ -303,13 +341,17 @@ ecdh_encrypt_pkcs5(rnp::RNG * rng,
ret = ecdh_rfc3394_wrap(
out->m, &out->mlen, mpad.data(), m_padded_len, kek.data(), key->key_wrap_alg);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to wrap key.");
goto done;
+ /* LCOV_EXCL_END */
}
/* write ephemeral public key */
if (!ec_write_pubkey(ephkey, out->p, key->curve)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to write ec key.");
goto done;
+ /* LCOV_EXCL_END */
}
ret = RNP_SUCCESS;
done:
@@ -351,32 +393,42 @@ ecdh_decrypt_pkcs5(uint8_t * out,
rnp_result_t ret = RNP_ERROR_GENERIC;
EVP_PKEY * pkey = ec_load_key(key->p, &key->x, key->curve);
if (!pkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to load secret key.");
ret = RNP_ERROR_BAD_PARAMETERS;
goto done;
+ /* LCOV_EXCL_END */
}
/* do ECDH derivation */
if (!ecdh_derive_secret(pkey, ephkey, sec.data(), &seclen)) {
+ /* LCOV_EXCL_START */
RNP_LOG("ECDH derivation failed.");
goto done;
+ /* LCOV_EXCL_END */
}
/* here we got x value in sec, deriving kek */
ret = ecdh_derive_kek(sec.data(), seclen, *key, fingerprint, kek.data(), keklen);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to derive KEK.");
goto done;
+ /* LCOV_EXCL_END */
}
/* do RFC 3394 AES key unwrap */
ret = ecdh_rfc3394_unwrap(
mpad.data(), &mpadlen, in->m, in->mlen, kek.data(), key->key_wrap_alg);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to unwrap key.");
goto done;
+ /* LCOV_EXCL_END */
}
/* remove PKCS#7 padding */
if (!unpad_pkcs7(mpad.data(), mpadlen, &mpadlen)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to unpad key.");
goto done;
+ /* LCOV_EXCL_END */
}
assert(mpadlen <= *out_len);
*out_len = mpadlen;
diff --git a/src/lib/crypto/elgamal_ossl.cpp b/src/lib/crypto/elgamal_ossl.cpp
index f3fa381..6049ad2 100644
--- a/src/lib/crypto/elgamal_ossl.cpp
+++ b/src/lib/crypto/elgamal_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021, 2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -38,6 +38,10 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#endif
// Max supported key byte size
#define ELGAMAL_MAX_P_BYTELEN BITS_TO_BYTES(PGP_MPINT_BITS)
@@ -47,8 +51,10 @@ elgamal_validate_key(const pgp_eg_key_t *key, bool secret)
{
BN_CTX *ctx = BN_CTX_new();
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed.");
return false;
+ /* LCOV_EXCL_END */
}
BN_CTX_start(ctx);
bool res = false;
@@ -86,8 +92,10 @@ elgamal_validate_key(const pgp_eg_key_t *key, bool secret)
}
for (size_t i = 2; i < (1 << 17); i++) {
if (!BN_mod_mul_reciprocal(r, r, g, rctx, ctx)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Multiplication failed.");
goto done;
+ /* LCOV_EXCL_END */
}
if (BN_cmp(r, BN_value_one()) == 0) {
RNP_LOG("Small subgroup detected. Order %zu", i);
@@ -135,8 +143,10 @@ pkcs1v15_pad(uint8_t *out, size_t out_len, const uint8_t *in, size_t in_len)
while (!out[i] && (cntr--) && (RAND_bytes(&out[i], 1) == 1)) {
}
if (!out[i]) {
+ /* LCOV_EXCL_START */
RNP_LOG("Something is wrong with RNG.");
return false;
+ /* LCOV_EXCL_END */
}
}
memcpy(out + rnd + 3, in, in_len);
@@ -176,14 +186,18 @@ elgamal_encrypt_pkcs1(rnp::RNG * rng,
pgp_mpi_t mm = {};
mm.len = key->p.len;
if (!pkcs1v15_pad(mm.mpi, mm.len, in, in_len)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to add PKCS1 v1.5 padding.");
return RNP_ERROR_BAD_PARAMETERS;
+ /* LCOV_EXCL_END */
}
rnp_result_t ret = RNP_ERROR_GENERIC;
BN_CTX * ctx = BN_CTX_new();
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed.");
return RNP_ERROR_OUT_OF_MEMORY;
+ /* LCOV_EXCL_END */
}
BN_CTX_start(ctx);
BN_MONT_CTX *mctx = BN_MONT_CTX_new();
@@ -195,41 +209,55 @@ elgamal_encrypt_pkcs1(rnp::RNG * rng,
bignum_t * c2 = BN_CTX_get(ctx);
bignum_t * k = BN_secure_new();
if (!mctx || !m || !p || !g || !y || !c1 || !c2 || !k) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed.");
ret = RNP_ERROR_OUT_OF_MEMORY;
goto done;
+ /* LCOV_EXCL_END */
}
/* initialize Montgomery context */
if (BN_MONT_CTX_set(mctx, p, ctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to setup Montgomery context.");
goto done;
+ /* LCOV_EXCL_END */
}
int res;
/* must not fail */
res = BN_rshift1(c1, p);
assert(res == 1);
if (res < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("BN_rshift1 failed.");
goto done;
+ /* LCOV_EXCL_END */
}
/* generate k */
if (BN_rand_range(k, c1) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to generate k.");
goto done;
+ /* LCOV_EXCL_END */
}
/* calculate c1 = g ^ k (mod p) */
if (BN_mod_exp_mont_consttime(c1, g, k, p, ctx, mctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Exponentiation 1 failed");
goto done;
+ /* LCOV_EXCL_END */
}
/* calculate c2 = m * y ^ k (mod p)*/
if (BN_mod_exp_mont_consttime(c2, y, k, p, ctx, mctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Exponentiation 2 failed");
goto done;
+ /* LCOV_EXCL_END */
}
if (BN_mod_mul(c2, c2, m, p, ctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Multiplication failed");
goto done;
+ /* LCOV_EXCL_END */
}
res = bn2mpi(c1, &out->g) && bn2mpi(c2, &out->m);
assert(res == 1);
@@ -258,8 +286,10 @@ elgamal_decrypt_pkcs1(rnp::RNG * rng,
}
BN_CTX *ctx = BN_CTX_new();
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed.");
return RNP_ERROR_OUT_OF_MEMORY;
+ /* LCOV_EXCL_END */
}
pgp_mpi_t mm = {};
size_t padlen = 0;
@@ -274,37 +304,49 @@ elgamal_decrypt_pkcs1(rnp::RNG * rng,
bignum_t * s = BN_CTX_get(ctx);
bignum_t * m = BN_secure_new();
if (!mctx || !p || !g || !x || !c1 || !c2 || !m) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed.");
ret = RNP_ERROR_OUT_OF_MEMORY;
goto done;
+ /* LCOV_EXCL_END */
}
/* initialize Montgomery context */
if (BN_MONT_CTX_set(mctx, p, ctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to setup Montgomery context.");
goto done;
+ /* LCOV_EXCL_END */
}
/* calculate s = c1 ^ x (mod p) */
if (BN_mod_exp_mont_consttime(s, c1, x, p, ctx, mctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Exponentiation 1 failed");
goto done;
+ /* LCOV_EXCL_END */
}
/* calculate s^-1 (mod p) */
BN_set_flags(s, BN_FLG_CONSTTIME);
if (!BN_mod_inverse(s, s, p, ctx)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to calculate inverse.");
goto done;
+ /* LCOV_EXCL_END */
}
/* calculate m = c2 * s ^ -1 (mod p)*/
if (BN_mod_mul(m, c2, s, p, ctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Multiplication failed");
goto done;
+ /* LCOV_EXCL_END */
}
bool res;
res = bn2mpi(m, &mm);
assert(res);
if (!res) {
+ /* LCOV_EXCL_START */
RNP_LOG("bn2mpi failed.");
goto done;
+ /* LCOV_EXCL_END */
}
/* unpad, handling skipped leftmost 0 case */
if (!pkcs1v15_unpad(&padlen, mm.mpi, mm.len, mm.len == key->p.len - 1)) {
@@ -334,8 +376,10 @@ elgamal_generate(rnp::RNG *rng, pgp_eg_key_t *key, size_t keybits)
return RNP_ERROR_BAD_PARAMETERS;
}
- rnp_result_t ret = RNP_ERROR_GENERIC;
- const DH * dh = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+#if !defined(CRYPTO_BACKEND_OPENSSL3)
+ const DH *dh = NULL;
+#endif
EVP_PKEY * pkey = NULL;
EVP_PKEY * parmkey = NULL;
EVP_PKEY_CTX *ctx = NULL;
@@ -343,47 +387,93 @@ elgamal_generate(rnp::RNG *rng, pgp_eg_key_t *key, size_t keybits)
/* Generate DH params, which usable for ElGamal as well */
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DH, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
return ret;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_paramgen_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, keybits) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set key bits: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
/* OpenSSL correctly handles case with g = 5, making sure that g is primitive root of
* q-group */
if (EVP_PKEY_CTX_set_dh_paramgen_generator(ctx, DH_GENERATOR_5) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set key generator: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_paramgen(ctx, &parmkey) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to generate parameters: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
EVP_PKEY_CTX_free(ctx);
/* Generate DH (ElGamal) key */
start:
ctx = EVP_PKEY_CTX_new(parmkey, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_keygen_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("ElGamal keygen failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
+ }
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ {
+ rnp::bn y;
+ if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, y.ptr())) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("Failed to retrieve ElGamal public key: %lu", ERR_peek_last_error());
+ goto done;
+ /* LCOV_EXCL_END */
+ }
+ if (y.bytes() != BITS_TO_BYTES(keybits)) {
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto start;
+ }
+
+ rnp::bn p;
+ rnp::bn g;
+ rnp::bn x;
+ bool res = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_P, p.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_G, g.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, x.ptr());
+ if (res && p.mpi(key->p) && g.mpi(key->g) && y.mpi(key->y) && x.mpi(key->x)) {
+ ret = RNP_SUCCESS;
+ }
}
+#else
dh = EVP_PKEY_get0_DH(pkey);
if (!dh) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to retrieve DH key: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (BITS_TO_BYTES(BN_num_bits(DH_get0_pub_key(dh))) != BITS_TO_BYTES(keybits)) {
EVP_PKEY_CTX_free(ctx);
@@ -402,14 +492,17 @@ start:
y = DH_get0_pub_key(dh);
x = DH_get0_priv_key(dh);
if (!p || !g || !y || !x) {
+ /* LCOV_EXCL_START */
ret = RNP_ERROR_BAD_STATE;
goto done;
+ /* LCOV_EXCL_END */
}
bn2mpi(p, &key->p);
bn2mpi(g, &key->g);
bn2mpi(y, &key->y);
bn2mpi(x, &key->x);
ret = RNP_SUCCESS;
+#endif
done:
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(parmkey);
diff --git a/src/lib/crypto/rsa.cpp b/src/lib/crypto/rsa.cpp
index f7ddefe..83fa044 100644
--- a/src/lib/crypto/rsa.cpp
+++ b/src/lib/crypto/rsa.cpp
@@ -333,12 +333,16 @@ rsa_decrypt_pkcs1(rnp::RNG * rng,
return RNP_ERROR_OUT_OF_MEMORY;
}
+ size_t skip = 0;
if (botan_pk_op_decrypt_create(&decrypt_op, rsa_key, "PKCS1v15", 0)) {
goto done;
}
-
+ /* Skip trailing zeroes if any as Botan3 doesn't like m.len > e.len */
+ while ((in->m.len - skip > key->e.len) && !in->m.mpi[skip]) {
+ skip++;
+ }
*out_len = PGP_MPINT_SIZE;
- if (botan_pk_op_decrypt(decrypt_op, out, out_len, in->m.mpi, in->m.len)) {
+ if (botan_pk_op_decrypt(decrypt_op, out, out_len, in->m.mpi + skip, in->m.len - skip)) {
goto done;
}
ret = RNP_SUCCESS;
diff --git a/src/lib/crypto/rsa_ossl.cpp b/src/lib/crypto/rsa_ossl.cpp
index 24cff29..445974c 100644
--- a/src/lib/crypto/rsa_ossl.cpp
+++ b/src/lib/crypto/rsa_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021-2023, [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -45,30 +45,29 @@
static RSA *
rsa_load_public_key(const pgp_rsa_key_t *key)
{
- RSA * rsa = NULL;
- bignum_t *n = mpi2bn(&key->n);
- bignum_t *e = mpi2bn(&key->e);
+ rnp::bn n(key->n);
+ rnp::bn e(key->e);
- if (!n || !e) {
+ if (!n.get() || !e.get()) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- rsa = RSA_new();
+ RSA *rsa = RSA_new();
if (!rsa) {
+ /* LCOV_EXCL_START */
RNP_LOG("Out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- if (RSA_set0_key(rsa, n, e, NULL) != 1) {
+ /* OpenSSL set0 function transfers ownership of bignums */
+ if (RSA_set0_key(rsa, n.own(), e.own(), NULL) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Public key load error: %lu", ERR_peek_last_error());
RSA_free(rsa);
- rsa = NULL;
- goto done;
- }
-done:
- /* OpenSSL set0 function transfers ownership of bignums */
- if (!rsa) {
- bn_free(n);
- bn_free(e);
+ return NULL;
+ /* LCOV_EXCL_END */
}
return rsa;
}
@@ -76,44 +75,41 @@ done:
static RSA *
rsa_load_secret_key(const pgp_rsa_key_t *key)
{
- RSA * rsa = NULL;
- bignum_t *n = mpi2bn(&key->n);
- bignum_t *e = mpi2bn(&key->e);
- bignum_t *p = mpi2bn(&key->p);
- bignum_t *q = mpi2bn(&key->q);
- bignum_t *d = mpi2bn(&key->d);
+ rnp::bn n(key->n);
+ rnp::bn e(key->e);
+ rnp::bn p(key->p);
+ rnp::bn q(key->q);
+ rnp::bn d(key->d);
- if (!n || !p || !q || !e || !d) {
+ if (!n.get() || !p.get() || !q.get() || !e.get() || !d.get()) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- rsa = RSA_new();
+ RSA *rsa = RSA_new();
if (!rsa) {
+ /* LCOV_EXCL_START */
RNP_LOG("Out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- if (RSA_set0_key(rsa, n, e, d) != 1) {
+ /* OpenSSL set0 function transfers ownership of bignums */
+ if (RSA_set0_key(rsa, n.own(), e.own(), d.own()) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Secret key load error: %lu", ERR_peek_last_error());
RSA_free(rsa);
- rsa = NULL;
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
/* OpenSSL has p < q, as we do */
- if (RSA_set0_factors(rsa, p, q) != 1) {
+ if (RSA_set0_factors(rsa, p.own(), q.own()) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Factors load error: %lu", ERR_peek_last_error());
RSA_free(rsa);
- rsa = NULL;
- goto done;
- }
-done:
- /* OpenSSL set0 function transfers ownership of bignums */
- if (!rsa) {
- bn_free(n);
- bn_free(p);
- bn_free(q);
- bn_free(e);
- bn_free(d);
+ return NULL;
+ /* LCOV_EXCL_END */
}
return rsa;
}
@@ -123,8 +119,10 @@ rsa_init_context(const pgp_rsa_key_t *key, bool secret)
{
EVP_PKEY *evpkey = EVP_PKEY_new();
if (!evpkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("allocation failed");
return NULL;
+ /* LCOV_EXCL_END */
}
EVP_PKEY_CTX *ctx = NULL;
RSA * rsakey = secret ? rsa_load_secret_key(key) : rsa_load_public_key(key);
@@ -132,12 +130,14 @@ rsa_init_context(const pgp_rsa_key_t *key, bool secret)
goto done;
}
if (EVP_PKEY_set1_RSA(evpkey, rsakey) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set key: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
ctx = EVP_PKEY_CTX_new(evpkey, NULL);
if (!ctx) {
- RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
+ RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
}
done:
RSA_free(rsakey);
@@ -150,70 +150,72 @@ rsa_bld_params(const pgp_rsa_key_t *key, bool secret)
{
OSSL_PARAM * params = NULL;
OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
- bignum_t * n = mpi2bn(&key->n);
- bignum_t * e = mpi2bn(&key->e);
- bignum_t * d = NULL;
- bignum_t * p = NULL;
- bignum_t * q = NULL;
- bignum_t * u = NULL;
+ rnp::bn n(key->n);
+ rnp::bn e(key->e);
+ rnp::bn d;
+ rnp::bn p;
+ rnp::bn q;
+ rnp::bn u;
BN_CTX * bnctx = NULL;
- if (!n || !e || !bld) {
+ if (!n.get() || !e.get() || !bld) {
+ /* LCOV_EXCL_START */
RNP_LOG("Out of memory");
goto done;
+ /* LCOV_EXCL_END */
}
- if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n) ||
- !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e)) {
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n.get()) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e.get())) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to push RSA params.");
- goto done;
+ OSSL_PARAM_BLD_free(bld);
+ return NULL;
+ /* LCOV_EXCL_END */
}
if (secret) {
- d = mpi2bn(&key->d);
+ d.set(key->d);
/* As we have u = p^-1 mod q, and qInv = q^-1 mod p, we need to replace one with
* another */
- p = mpi2bn(&key->q);
- q = mpi2bn(&key->p);
- u = mpi2bn(&key->u);
- if (!d || !p || !q || !u) {
+ p.set(key->q);
+ q.set(key->p);
+ u.set(key->u);
+ if (!d.get() || !p.get() || !q.get() || !u.get()) {
goto done;
}
/* We need to calculate exponents manually */
bnctx = BN_CTX_new();
if (!bnctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to allocate BN_CTX.");
goto done;
+ /* LCOV_EXCL_END */
}
bignum_t *p1 = BN_CTX_get(bnctx);
bignum_t *q1 = BN_CTX_get(bnctx);
bignum_t *dp = BN_CTX_get(bnctx);
bignum_t *dq = BN_CTX_get(bnctx);
- if (!BN_copy(p1, p) || !BN_sub_word(p1, 1) || !BN_copy(q1, q) || !BN_sub_word(q1, 1) ||
- !BN_mod(dp, d, p1, bnctx) || !BN_mod(dq, d, q1, bnctx)) {
- RNP_LOG("Failed to calculate dP or dQ.");
+ if (!BN_copy(p1, p.get()) || !BN_sub_word(p1, 1) || !BN_copy(q1, q.get()) ||
+ !BN_sub_word(q1, 1) || !BN_mod(dp, d.get(), p1, bnctx) ||
+ !BN_mod(dq, d.get(), q1, bnctx)) {
+ RNP_LOG("Failed to calculate dP or dQ."); // LCOV_EXCL_LINE
}
/* Push params */
- if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, d) ||
- !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1, p) ||
- !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2, q) ||
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, d.get()) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1, p.get()) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2, q.get()) ||
!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT1, dp) ||
!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT2, dq) ||
- !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, u)) {
- RNP_LOG("Failed to push RSA secret params.");
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, u.get())) {
+ RNP_LOG("Failed to push RSA secret params."); // LCOV_EXCL_LINE
goto done;
}
}
params = OSSL_PARAM_BLD_to_param(bld);
if (!params) {
- RNP_LOG("Failed to build RSA params: %s.", ossl_latest_err());
+ RNP_LOG("Failed to build RSA params: %s.", ossl_latest_err()); // LCOV_EXCL_LINE
}
done:
- bn_free(n);
- bn_free(e);
- bn_free(d);
- bn_free(p);
- bn_free(q);
- bn_free(u);
BN_CTX_free(bnctx);
OSSL_PARAM_BLD_free(bld);
return params;
@@ -231,17 +233,21 @@ rsa_load_key(const pgp_rsa_key_t *key, bool secret)
EVP_PKEY * res = NULL;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %s", ossl_latest_err());
goto done;
+ /* LCOV_EXCL_END */
}
/* Create key */
if (EVP_PKEY_fromdata_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to initialize key creation: %s", ossl_latest_err());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_fromdata(
ctx, &res, secret ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY, params) <= 0) {
- RNP_LOG("Failed to create RSA key: %s", ossl_latest_err());
+ RNP_LOG("Failed to create RSA key: %s", ossl_latest_err()); // LCOV_EXCL_LINE
}
done:
EVP_PKEY_CTX_free(ctx);
@@ -258,7 +264,7 @@ rsa_init_context(const pgp_rsa_key_t *key, bool secret)
}
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx) {
- RNP_LOG("Context allocation failed: %s", ossl_latest_err());
+ RNP_LOG("Context allocation failed: %s", ossl_latest_err()); // LCOV_EXCL_LINE
}
EVP_PKEY_free(pkey);
return ctx;
@@ -271,12 +277,14 @@ rsa_validate_key(rnp::RNG *rng, const pgp_rsa_key_t *key, bool secret)
#ifdef CRYPTO_BACKEND_OPENSSL3
EVP_PKEY_CTX *ctx = rsa_init_context(key, secret);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init context: %s", ossl_latest_err());
return RNP_ERROR_GENERIC;
+ /* LCOV_EXCL_END */
}
int res = secret ? EVP_PKEY_pairwise_check(ctx) : EVP_PKEY_public_check(ctx);
if (res <= 0) {
- RNP_LOG("Key validation error: %s", ossl_latest_err());
+ RNP_LOG("Key validation error: %s", ossl_latest_err()); // LCOV_EXCL_LINE
}
EVP_PKEY_CTX_free(ctx);
return res > 0 ? RNP_SUCCESS : RNP_ERROR_GENERIC;
@@ -284,34 +292,33 @@ rsa_validate_key(rnp::RNG *rng, const pgp_rsa_key_t *key, bool secret)
if (secret) {
EVP_PKEY_CTX *ctx = rsa_init_context(key, secret);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init context: %s", ossl_latest_err());
return RNP_ERROR_GENERIC;
+ /* LCOV_EXCL_END */
}
int res = EVP_PKEY_check(ctx);
if (res <= 0) {
- RNP_LOG("Key validation error: %s", ossl_latest_err());
+ RNP_LOG("Key validation error: %s", ossl_latest_err()); // LCOV_EXCL_LINE
}
EVP_PKEY_CTX_free(ctx);
return res > 0 ? RNP_SUCCESS : RNP_ERROR_GENERIC;
}
/* OpenSSL 1.1.1 doesn't have RSA public key check function, so let's do some checks */
- rnp_result_t ret = RNP_ERROR_GENERIC;
- bignum_t * n = mpi2bn(&key->n);
- bignum_t * e = mpi2bn(&key->e);
- if (!n || !e) {
+ rnp::bn n(key->n);
+ rnp::bn e(key->e);
+ if (!n.get() || !e.get()) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- ret = RNP_ERROR_OUT_OF_MEMORY;
- goto done;
+ return RNP_ERROR_OUT_OF_MEMORY;
+ /* LCOV_EXCL_END */
}
- if ((BN_num_bits(n) < 512) || !BN_is_odd(n) || (BN_num_bits(e) < 2) || !BN_is_odd(e)) {
- goto done;
+ if ((BN_num_bits(n.get()) < 512) || !BN_is_odd(n.get()) || (BN_num_bits(e.get()) < 2) ||
+ !BN_is_odd(e.get())) {
+ return RNP_ERROR_GENERIC;
}
- ret = RNP_SUCCESS;
-done:
- bn_free(n);
- bn_free(e);
- return ret;
+ return RNP_SUCCESS;
#endif
}
@@ -528,6 +535,97 @@ done:
return ret;
}
+static bool
+rsa_calculate_pqu(const bignum_t *p, const bignum_t *q, const bignum_t *u, pgp_rsa_key_t &key)
+{
+ /* OpenSSL doesn't care whether p < q */
+ if (BN_cmp(p, q) > 0) {
+ /* In this case we have u, as iqmp is inverse of q mod p, and we exchange them */
+ bn2mpi(q, &key.p);
+ bn2mpi(p, &key.q);
+ bn2mpi(u, &key.u);
+ return true;
+ }
+
+ BN_CTX *bnctx = BN_CTX_new();
+ if (!bnctx) {
+ return false;
+ }
+
+ /* we need to calculate u, since we need inverse of p mod q, while OpenSSL has inverse of q
+ * mod p, and doesn't care of p < q */
+ BN_CTX_start(bnctx);
+ bignum_t *nu = BN_CTX_get(bnctx);
+ bignum_t *nq = BN_CTX_get(bnctx);
+ if (!nu || !nq) {
+ BN_CTX_free(bnctx);
+ return false;
+ }
+ BN_with_flags(nq, q, BN_FLG_CONSTTIME);
+ /* calculate inverse of p mod q */
+ if (!BN_mod_inverse(nu, p, nq, bnctx)) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("Failed to calculate u");
+ BN_CTX_free(bnctx);
+ return false;
+ /* LCOV_EXCL_END */
+ }
+ bn2mpi(p, &key.p);
+ bn2mpi(q, &key.q);
+ bn2mpi(nu, &key.u);
+ BN_CTX_free(bnctx);
+ return true;
+}
+
+static bool
+rsa_extract_key(EVP_PKEY *pkey, pgp_rsa_key_t &key)
+{
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ rnp::bn n;
+ rnp::bn e;
+ rnp::bn d;
+ rnp::bn p;
+ rnp::bn q;
+ rnp::bn u;
+
+ bool res = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, n.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, e.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_D, d.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, p.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, q.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, u.ptr()) &&
+ rsa_calculate_pqu(p.get(), q.get(), u.get(), key);
+ return res && n.mpi(key.n) && e.mpi(key.e) && d.mpi(key.d);
+#else
+ const RSA *rsa = EVP_PKEY_get0_RSA(pkey);
+ if (!rsa) {
+ RNP_LOG("Failed to retrieve RSA key: %lu", ERR_peek_last_error());
+ return false;
+ }
+ if (RSA_check_key(rsa) != 1) {
+ RNP_LOG("Key validation error: %lu", ERR_peek_last_error());
+ return false;
+ }
+
+ const bignum_t *n = RSA_get0_n(rsa);
+ const bignum_t *e = RSA_get0_e(rsa);
+ const bignum_t *d = RSA_get0_d(rsa);
+ const bignum_t *p = RSA_get0_p(rsa);
+ const bignum_t *q = RSA_get0_q(rsa);
+ const bignum_t *u = RSA_get0_iqmp(rsa);
+ if (!n || !e || !d || !p || !q || !u) {
+ return false;
+ }
+ if (!rsa_calculate_pqu(p, q, u, key)) {
+ return false;
+ }
+ bn2mpi(n, &key.n);
+ bn2mpi(e, &key.e);
+ bn2mpi(d, &key.d);
+ return true;
+#endif
+}
+
rnp_result_t
rsa_generate(rnp::RNG *rng, pgp_rsa_key_t *key, size_t numbits)
{
@@ -535,12 +633,9 @@ rsa_generate(rnp::RNG *rng, pgp_rsa_key_t *key, size_t numbits)
return RNP_ERROR_BAD_PARAMETERS;
}
- rnp_result_t ret = RNP_ERROR_GENERIC;
- const RSA * rsa = NULL;
- EVP_PKEY * pkey = NULL;
- EVP_PKEY_CTX * ctx = NULL;
- const bignum_t *u = NULL;
- BN_CTX * bnctx = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ EVP_PKEY * pkey = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (!ctx) {
@@ -559,71 +654,11 @@ rsa_generate(rnp::RNG *rng, pgp_rsa_key_t *key, size_t numbits)
RNP_LOG("RSA keygen failed: %lu", ERR_peek_last_error());
goto done;
}
- rsa = EVP_PKEY_get0_RSA(pkey);
- if (!rsa) {
- RNP_LOG("Failed to retrieve RSA key: %lu", ERR_peek_last_error());
- goto done;
- }
- if (RSA_check_key(rsa) != 1) {
- RNP_LOG("Key validation error: %lu", ERR_peek_last_error());
- goto done;
- }
-
- const bignum_t *n;
- const bignum_t *e;
- const bignum_t *p;
- const bignum_t *q;
- const bignum_t *d;
- n = RSA_get0_n(rsa);
- e = RSA_get0_e(rsa);
- d = RSA_get0_d(rsa);
- p = RSA_get0_p(rsa);
- q = RSA_get0_q(rsa);
- if (!n || !e || !d || !p || !q) {
- ret = RNP_ERROR_OUT_OF_MEMORY;
- goto done;
+ if (rsa_extract_key(pkey, *key)) {
+ ret = RNP_SUCCESS;
}
- /* OpenSSL doesn't care whether p < q */
- if (BN_cmp(p, q) > 0) {
- /* In this case we have u, as iqmp is inverse of q mod p, and we exchange them */
- const bignum_t *tmp = p;
- p = q;
- q = tmp;
- u = RSA_get0_iqmp(rsa);
- } else {
- /* we need to calculate u, since we need inverse of p mod q, while OpenSSL has inverse
- * of q mod p, and doesn't care of p < q */
- bnctx = BN_CTX_new();
- if (!bnctx) {
- ret = RNP_ERROR_OUT_OF_MEMORY;
- goto done;
- }
- BN_CTX_start(bnctx);
- bignum_t *nu = BN_CTX_get(bnctx);
- bignum_t *nq = BN_CTX_get(bnctx);
- if (!nu || !nq) {
- ret = RNP_ERROR_OUT_OF_MEMORY;
- goto done;
- }
- BN_with_flags(nq, q, BN_FLG_CONSTTIME);
- /* calculate inverse of p mod q */
- if (!BN_mod_inverse(nu, p, nq, bnctx)) {
- RNP_LOG("Failed to calculate u");
- ret = RNP_ERROR_BAD_STATE;
- goto done;
- }
- u = nu;
- }
- bn2mpi(n, &key->n);
- bn2mpi(e, &key->e);
- bn2mpi(p, &key->p);
- bn2mpi(q, &key->q);
- bn2mpi(d, &key->d);
- bn2mpi(u, &key->u);
- ret = RNP_SUCCESS;
done:
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey);
- BN_CTX_free(bnctx);
return ret;
}
diff --git a/src/lib/crypto/symmetric.cpp b/src/lib/crypto/symmetric.cpp
index aeed784..0f7b259 100644
--- a/src/lib/crypto/symmetric.cpp
+++ b/src/lib/crypto/symmetric.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023, [Ribose Inc](https://www.ribose.com).
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
@@ -57,6 +57,7 @@
#include <stdlib.h>
#include <assert.h>
#include <botan/ffi.h>
+#include <botan/build.h>
#include "utils.h"
static const char *
@@ -224,7 +225,7 @@ pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
uint64_t buf64[512]; // 4KB - page size
uint64_t iv64[2];
size_t blocks, blockb;
- unsigned blsize = crypt->blocksize;
+ size_t blsize = crypt->blocksize;
/* encrypting till the block boundary */
while (bytes && crypt->cfb.remaining) {
@@ -306,7 +307,7 @@ pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
uint64_t outbuf64[512];
uint64_t iv64[2];
size_t blocks, blockb;
- unsigned blsize = crypt->blocksize;
+ size_t blsize = crypt->blocksize;
/* decrypting till the block boundary */
while (bytes && crypt->cfb.remaining) {
@@ -613,7 +614,10 @@ pgp_cipher_aead_finish(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
void
pgp_cipher_aead_destroy(pgp_crypt_t *crypt)
{
- botan_cipher_destroy(crypt->aead.obj);
+ if (crypt->aead.obj) {
+ botan_cipher_destroy(crypt->aead.obj);
+ }
+ memset(crypt, 0x0, sizeof(*crypt));
}
size_t
diff --git a/src/lib/crypto/symmetric_ossl.cpp b/src/lib/crypto/symmetric_ossl.cpp
index 98e90ed..ff19362 100644
--- a/src/lib/crypto/symmetric_ossl.cpp
+++ b/src/lib/crypto/symmetric_ossl.cpp
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2021 Ribose Inc.
+ * Copyright (c) 2021-2023 Ribose Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -94,8 +94,10 @@ pgp_cipher_cfb_start(pgp_crypt_t * crypt,
const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_name);
if (!cipher) {
+ /* LCOV_EXCL_START */
RNP_LOG("Cipher %s is not supported by OpenSSL.", cipher_name);
return false;
+ /* LCOV_EXCL_END */
}
crypt->alg = alg;
@@ -104,9 +106,11 @@ pgp_cipher_cfb_start(pgp_crypt_t * crypt,
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
int res = EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv);
if (res != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to initialize cipher.");
EVP_CIPHER_CTX_free(ctx);
return false;
+ /* LCOV_EXCL_END */
}
crypt->cfb.obj = ctx;
@@ -131,7 +135,7 @@ int
pgp_cipher_cfb_finish(pgp_crypt_t *crypt)
{
if (!crypt) {
- return 0;
+ return 0; // LCOV_EXCL_LINE
}
if (crypt->cfb.obj) {
EVP_CIPHER_CTX_free(crypt->cfb.obj);
@@ -149,7 +153,7 @@ pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
uint64_t buf64[512]; // 4KB - page size
uint64_t iv64[2];
size_t blocks, blockb;
- unsigned blsize = crypt->blocksize;
+ size_t blsize = crypt->blocksize;
/* encrypting till the block boundary */
while (bytes && crypt->cfb.remaining) {
@@ -182,7 +186,7 @@ pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
EVP_EncryptUpdate(
crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 16);
if (outlen != 16) {
- RNP_LOG("Bad outlen: must be 16");
+ RNP_LOG("Bad outlen: must be 16"); // LCOV_EXCL_LINE
}
*in64 ^= iv64[0];
iv64[0] = *in64++;
@@ -196,7 +200,7 @@ pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
EVP_EncryptUpdate(
crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 8);
if (outlen != 8) {
- RNP_LOG("Bad outlen: must be 8");
+ RNP_LOG("Bad outlen: must be 8"); // LCOV_EXCL_LINE
}
*in64 ^= iv64[0];
iv64[0] = *in64++;
@@ -218,7 +222,7 @@ pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
int outlen = blsize;
EVP_EncryptUpdate(crypt->cfb.obj, crypt->cfb.iv, &outlen, crypt->cfb.iv, (int) blsize);
if (outlen != (int) blsize) {
- RNP_LOG("Bad outlen: must be %u", blsize);
+ RNP_LOG("Bad outlen: must be %zu", blsize); // LCOV_EXCL_LINE
}
crypt->cfb.remaining = blsize;
@@ -243,7 +247,7 @@ pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
uint64_t outbuf64[512];
uint64_t iv64[2];
size_t blocks, blockb;
- unsigned blsize = crypt->blocksize;
+ size_t blsize = crypt->blocksize;
/* decrypting till the block boundary */
while (bytes && crypt->cfb.remaining) {
@@ -279,7 +283,7 @@ pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
EVP_EncryptUpdate(
crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 16);
if (outlen != 16) {
- RNP_LOG("Bad outlen: must be 16");
+ RNP_LOG("Bad outlen: must be 16"); // LCOV_EXCL_LINE
}
*out64++ = *in64 ^ iv64[0];
iv64[0] = *in64++;
@@ -293,7 +297,7 @@ pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
EVP_EncryptUpdate(
crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 8);
if (outlen != 8) {
- RNP_LOG("Bad outlen: must be 8");
+ RNP_LOG("Bad outlen: must be 8"); // LCOV_EXCL_LINE
}
*out64++ = *in64 ^ iv64[0];
iv64[0] = *in64++;
@@ -315,7 +319,7 @@ pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
int outlen = blsize;
EVP_EncryptUpdate(crypt->cfb.obj, crypt->cfb.iv, &outlen, crypt->cfb.iv, (int) blsize);
if (outlen != (int) blsize) {
- RNP_LOG("Bad outlen: must be %u", blsize);
+ RNP_LOG("Bad outlen: must be %zu", blsize); // LCOV_EXCL_LINE
}
crypt->cfb.remaining = blsize;
@@ -435,14 +439,18 @@ pgp_cipher_aead_init(pgp_crypt_t * crypt,
}
auto cipher = EVP_get_cipherbyname(algname);
if (!cipher) {
+ /* LCOV_EXCL_START */
RNP_LOG("Cipher %s is not supported.", algname);
return false;
+ /* LCOV_EXCL_END */
}
/* Create and setup context */
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create cipher context: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
crypt->aead.key = new rnp::secure_vector<uint8_t>(key, key + pgp_key_size(ealg));
@@ -510,21 +518,29 @@ pgp_cipher_aead_start(pgp_crypt_t *crypt, const uint8_t *nonce, size_t len)
assert(len == aead.n_len);
EVP_CIPHER_CTX_reset(ctx);
if (EVP_CipherInit_ex(ctx, aead.cipher, NULL, NULL, NULL, enc) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to initialize cipher: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, aead.n_len, NULL) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set nonce length: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
if (EVP_CipherInit_ex(ctx, NULL, NULL, aead.key->data(), nonce, enc) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to start cipher: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
int adlen = 0;
if (EVP_CipherUpdate(ctx, NULL, &adlen, aead.ad, aead.ad_len) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set AD: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
return true;
}
@@ -538,7 +554,7 @@ pgp_cipher_aead_update(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
int out_len = 0;
bool res = EVP_CipherUpdate(crypt->aead.obj, out, &out_len, in, len) == 1;
if (!res) {
- RNP_LOG("Failed to update cipher: %lu", ERR_peek_last_error());
+ RNP_LOG("Failed to update cipher: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
}
assert(out_len == (int) len);
return res;
@@ -558,26 +574,34 @@ pgp_cipher_aead_finish(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
if (aead.decrypt) {
assert(len >= aead.taglen);
if (len < aead.taglen) {
+ /* LCOV_EXCL_START */
RNP_LOG("Invalid state: too few input bytes.");
return false;
+ /* LCOV_EXCL_END */
}
size_t data_len = len - aead.taglen;
int out_len = 0;
if (EVP_CipherUpdate(ctx, out, &out_len, in, data_len) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to update cipher: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
uint8_t tag[PGP_AEAD_MAX_TAG_LEN] = {0};
memcpy(tag, in + data_len, aead.taglen);
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, aead.taglen, tag) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set tag: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
int out_len2 = 0;
if (EVP_CipherFinal_ex(ctx, out + out_len, &out_len2) != 1) {
/* Zero value if auth tag is incorrect */
if (ERR_peek_last_error()) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to finish AEAD decryption: %lu", ERR_peek_last_error());
+ /* LCOV_EXCL_END */
}
return false;
}
@@ -585,18 +609,24 @@ pgp_cipher_aead_finish(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
} else {
int out_len = 0;
if (EVP_CipherUpdate(ctx, out, &out_len, in, len) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to update cipher: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
int out_len2 = 0;
if (EVP_CipherFinal_ex(ctx, out + out_len, &out_len2) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to finish AEAD encryption: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
assert(out_len + out_len2 == (int) len);
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, aead.taglen, out + len) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to get tag: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
}
return true;
diff --git a/src/lib/rnp.cpp b/src/lib/rnp.cpp
index 24c46f9..9e92aaf 100644
--- a/src/lib/rnp.cpp
+++ b/src/lib/rnp.cpp
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2017-2021, Ribose Inc.
+ * Copyright (c) 2017-2023, Ribose Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -6197,6 +6197,20 @@ try {
FFI_GUARD
rnp_result_t
+rnp_signature_get_features(rnp_signature_handle_t handle, uint32_t *features)
+try {
+ if (!handle || !features) {
+ return RNP_ERROR_NULL_POINTER;
+ }
+ if (!handle->sig) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ *features = handle->sig->sig.key_get_features();
+ return RNP_SUCCESS;
+}
+FFI_GUARD
+
+rnp_result_t
rnp_signature_get_keyid(rnp_signature_handle_t handle, char **result)
try {
if (!handle || !result) {
@@ -7750,7 +7764,10 @@ key_to_json(json_object *jso, rnp_key_handle_t handle, uint32_t flags)
}
json_object_object_add(jso, "key wrap cipher", jsocipher);
}
+
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
case PGP_PKA_ECDSA:
case PGP_PKA_EDDSA:
case PGP_PKA_SM2: {
@@ -8313,7 +8330,9 @@ try {
}
pgp_armored_msg_t msgtype = PGP_ARMORED_UNKNOWN;
- if (is_armored_source(&input->src)) {
+ if (is_cleartext_source(&input->src)) {
+ msgtype = PGP_ARMORED_CLEARTEXT;
+ } else if (is_armored_source(&input->src)) {
msgtype = rnp_armored_get_type(&input->src);
} else {
msgtype = rnp_armor_guess_type(&input->src);
diff --git a/src/librekey/g23_sexp.hpp b/src/librekey/g23_sexp.hpp
index b888680..b062c52 100644
--- a/src/librekey/g23_sexp.hpp
+++ b/src/librekey/g23_sexp.hpp
@@ -27,8 +27,8 @@
#ifndef RNP_G23_SEXP_HPP
#define RNP_G23_SEXP_HPP
-#include "sexp/sexp.h"
-#include "sexp/ext-key-format.h"
+#include "sexpp/sexp.h"
+#include "sexpp/ext-key-format.h"
#define SXP_MAX_DEPTH 30
diff --git a/src/librepgp/stream-armor.cpp b/src/librepgp/stream-armor.cpp
index 669c305..b6669dc 100644
--- a/src/librepgp/stream-armor.cpp
+++ b/src/librepgp/stream-armor.cpp
@@ -235,7 +235,8 @@ armor_read_trailer(pgp_source_t *src)
size_t stlen;
pgp_source_armored_param_t *param = (pgp_source_armored_param_t *) src->param;
- if (!armor_skip_chars(param->readsrc, "\r\n")) {
+ /* Space or tab could get between armor and trailer, see issue #2199 */
+ if (!armor_skip_chars(param->readsrc, "\r\n \t")) {
return false;
}
@@ -1159,6 +1160,9 @@ is_armored_source(pgp_source_t *src)
return false;
}
buf[read - 1] = 0;
+ if (!!strstr((char *) buf, ST_CLEAR_BEGIN)) {
+ return false;
+ }
return !!strstr((char *) buf, ST_ARMOR_BEGIN);
}
diff --git a/src/librepgp/stream-parse.cpp b/src/librepgp/stream-parse.cpp
index 5ec4d64..f69f222 100644
--- a/src/librepgp/stream-parse.cpp
+++ b/src/librepgp/stream-parse.cpp
@@ -1727,11 +1727,11 @@ init_literal_src(pgp_source_t *src, pgp_source_t *readsrc)
case 'u':
case 'l':
case '1':
+ case 'm':
break;
default:
- RNP_LOG("unknown data format %" PRIu8, format);
- ret = RNP_ERROR_BAD_FORMAT;
- goto finish;
+ RNP_LOG("Warning: unknown data format %" PRIu8 ", ignoring.", format);
+ break;
}
param->hdr.format = format;
/* file name */
diff --git a/src/librepgp/stream-sig.cpp b/src/librepgp/stream-sig.cpp
index 6f3bc81..ab4098f 100644
--- a/src/librepgp/stream-sig.cpp
+++ b/src/librepgp/stream-sig.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2022, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2018-2023, [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -1005,6 +1005,13 @@ pgp_signature_t::set_revocation_reason(pgp_revocation_type_t code, const std::st
}
}
+pgp_key_feature_t
+pgp_signature_t::key_get_features() const
+{
+ const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_FEATURES);
+ return (pgp_key_feature_t)(subpkt ? subpkt->data[0] : 0);
+}
+
bool
pgp_signature_t::key_has_features(pgp_key_feature_t flags) const
{
@@ -1393,7 +1400,9 @@ pgp_signature_t::parse_material(pgp_signature_material_t &material) const
if (version < PGP_V4) {
RNP_LOG("Warning! v3 EdDSA signature.");
}
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
case PGP_PKA_ECDSA:
case PGP_PKA_SM2:
case PGP_PKA_ECDH:
diff --git a/src/librepgp/stream-sig.h b/src/librepgp/stream-sig.h
index 4f36c38..943efa9 100644
--- a/src/librepgp/stream-sig.h
+++ b/src/librepgp/stream-sig.h
@@ -274,6 +274,8 @@ typedef struct pgp_signature_t {
*/
void set_revocation_reason(pgp_revocation_type_t code, const std::string &reason);
+ pgp_key_feature_t key_get_features() const;
+
/**
* @brief Check whether signer's key supports certain feature(s). Makes sense only for
* self-signature, for more details see the RFC 4880bis, 5.2.3.25. If there is
diff --git a/src/rnp/fficli.cpp b/src/rnp/fficli.cpp
index fa118ee..b6e1b9a 100644
--- a/src/rnp/fficli.cpp
+++ b/src/rnp/fficli.cpp
@@ -2946,7 +2946,6 @@ cli_rnp_print_signatures(cli_rnp_t *rnp, const std::vector<rnp_op_verify_signatu
{
unsigned invalidc = 0;
unsigned unknownc = 0;
- unsigned validc = 0;
std::string title = "UNKNOWN signature";
FILE * resfp = rnp->resfp;
@@ -2955,7 +2954,6 @@ cli_rnp_print_signatures(cli_rnp_t *rnp, const std::vector<rnp_op_verify_signatu
switch (status) {
case RNP_SUCCESS:
title = "Good signature";
- validc++;
break;
case RNP_ERROR_SIGNATURE_EXPIRED:
title = "EXPIRED signature";
diff --git a/src/rnp/rnp.cpp b/src/rnp/rnp.cpp
index 30d3ac4..5dddaca 100644
--- a/src/rnp/rnp.cpp
+++ b/src/rnp/rnp.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2021 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023 [Ribose Inc](https://www.ribose.com).
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
@@ -297,7 +297,9 @@ setcmd(rnp_cfg &cfg, int cmd, const char *arg)
break;
case CMD_CLEARSIGN:
cfg.set_bool(CFG_CLEARTEXT, true);
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
case CMD_SIGN:
cfg.set_bool(CFG_NEEDSSECKEY, true);
cfg.set_bool(CFG_SIGN_NEEDED, true);
@@ -314,7 +316,9 @@ setcmd(rnp_cfg &cfg, int cmd, const char *arg)
case CMD_VERIFY:
/* single verify will discard output, decrypt will not */
cfg.set_bool(CFG_NO_OUTPUT, true);
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
case CMD_VERIFY_CAT:
newcmd = CMD_PROCESS;
break;
@@ -588,7 +592,9 @@ set_short_option(rnp_cfg &cfg, int ch, const char *arg)
cfg.set_bool(CFG_KEYSTORE_DISABLED, true);
break;
case 'h':
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
default:
return setcmd(cfg, CMD_HELP, optarg);
}
diff --git a/src/rnpkeys/main.cpp b/src/rnpkeys/main.cpp
index 8bcb7e1..3dd088c 100644
--- a/src/rnpkeys/main.cpp
+++ b/src/rnpkeys/main.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023, [Ribose Inc](https://www.ribose.com).
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
@@ -41,7 +41,7 @@
extern struct option options[];
extern const char * usage;
-optdefs_t
+static optdefs_t
get_short_cmd(int ch)
{
switch (ch) {
@@ -52,7 +52,9 @@ get_short_cmd(int ch)
case 'l':
return CMD_LIST_KEYS;
case 'h':
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
default:
return CMD_HELP;
}
diff --git a/src/rnpkeys/rnpkeys.cpp b/src/rnpkeys/rnpkeys.cpp
index 1a6997c..6a7885d 100644
--- a/src/rnpkeys/rnpkeys.cpp
+++ b/src/rnpkeys/rnpkeys.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2021, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023, [Ribose Inc](https://www.ribose.com).
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
@@ -260,8 +260,8 @@ import_keys(cli_rnp_t *rnp, rnp_input_t input, const std::string &inname)
} while (1);
// print statistics
- ERR_MSG("Import finished: %lu key%s processed, %lu new public keys, %lu new secret keys, "
- "%lu updated, %lu unchanged.",
+ ERR_MSG("Import finished: %zu key%s processed, %zu new public keys, %zu new secret keys, "
+ "%zu updated, %zu unchanged.",
processed_keys,
(processed_keys != 1) ? "s" : "",
new_pub_keys.size(),
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 7d2a6b0..d1a89d4 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -66,13 +66,26 @@ else()
endif()
find_package(JSON-C 0.11 REQUIRED)
-if (CRYPTO_BACKEND_LOWERCASE STREQUAL "botan")
- find_package(Botan2 2.14.0 REQUIRED)
+if (CRYPTO_BACKEND_BOTAN3)
+ find_package(Botan 3.0.0 REQUIRED)
+elseif (CRYPTO_BACKEND_BOTAN)
+ find_package(Botan 2.14.0 REQUIRED)
+ if(BOTAN_VERSION VERSION_GREATER_EQUAL 3.0.0)
+ set(CRYPTO_BACKEND_BOTAN3 1)
+ endif()
endif()
if (CRYPTO_BACKEND_LOWERCASE STREQUAL "openssl")
find_package(OpenSSL 1.1.1 REQUIRED)
endif()
+if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.8.5")
+ set(CMAKE_CXX_STANDARD 14)
+endif()
+
+if(CRYPTO_BACKEND_BOTAN3)
+ set(CMAKE_CXX_STANDARD 20)
+endif()
+
add_executable(rnp_tests
../rnp/rnpcfg.cpp
../rnp/fficli.cpp
@@ -170,13 +183,14 @@ target_include_directories(rnp_tests
PRIVATE
"${PROJECT_SOURCE_DIR}/src"
"${PROJECT_SOURCE_DIR}/src/lib"
- "${BOTAN2_INCLUDE_DIRS}"
+ "${BOTAN_INCLUDE_DIRS}"
+ "${SEXPP_INCLUDE_DIRS}"
)
target_link_libraries(rnp_tests
PRIVATE
librnp-static
JSON-C::JSON-C
- sexp
+ sexpp
${GTestMain}
)
if (CRYPTO_BACKEND_LOWERCASE STREQUAL "openssl")
@@ -220,6 +234,13 @@ function(add_cli_test suite)
"RNP_TESTS_GPG_PATH=${GPG_EXECUTABLE}"
"RNP_TESTS_GPGCONF_PATH=${GPGCONF_EXECUTABLE}"
)
+ if (CRYPTO_BACKEND_OPENSSL)
+ get_filename_component(ossl_root "${OPENSSL_INCLUDE_DIR}" DIRECTORY)
+ list(APPEND _env
+ "RNP_TESTS_OPENSSL_ROOT=${ossl_root}"
+ )
+ endif()
+
set_tests_properties(${_test_name} PROPERTIES
TIMEOUT 3000
FIXTURES_REQUIRED testdata
diff --git a/src/tests/cipher.cpp b/src/tests/cipher.cpp
index 25b98bf..3df5f0b 100644
--- a/src/tests/cipher.cpp
+++ b/src/tests/cipher.cpp
@@ -216,17 +216,15 @@ TEST_F(rnp_tests, rnp_test_x25519)
}
static void
-elgamal_roundtrip(pgp_eg_key_t *key)
+elgamal_roundtrip(pgp_eg_key_t *key, rnp::RNG &rng)
{
const uint8_t in_b[] = {0x01, 0x02, 0x03, 0x04, 0x17};
pgp_eg_encrypted_t enc = {{{0}}};
uint8_t res[1024];
size_t res_len = 0;
- assert_int_equal(elgamal_encrypt_pkcs1(&global_ctx.rng, &enc, in_b, sizeof(in_b), key),
- RNP_SUCCESS);
- assert_int_equal(elgamal_decrypt_pkcs1(&global_ctx.rng, res, &res_len, &enc, key),
- RNP_SUCCESS);
+ assert_int_equal(elgamal_encrypt_pkcs1(&rng, &enc, in_b, sizeof(in_b), key), RNP_SUCCESS);
+ assert_int_equal(elgamal_decrypt_pkcs1(&rng, res, &res_len, &enc, key), RNP_SUCCESS);
assert_int_equal(res_len, sizeof(in_b));
assert_true(bin_eq_hex(res, res_len, "0102030417"));
}
@@ -236,7 +234,7 @@ TEST_F(rnp_tests, raw_elgamal_random_key_test_success)
pgp_eg_key_t key;
assert_int_equal(elgamal_generate(&global_ctx.rng, &key, 1024), RNP_SUCCESS);
- elgamal_roundtrip(&key);
+ elgamal_roundtrip(&key, global_ctx.rng);
}
TEST_F(rnp_tests, ecdsa_signverify_success)
@@ -1011,3 +1009,89 @@ TEST_F(rnp_tests, test_brainpool_enabled)
assert_false(supported);
#endif
}
+
+#if defined(CRYPTO_BACKEND_BOTAN)
+TEST_F(rnp_tests, test_windows_botan_crash)
+{
+ /* Reproducer for https://github.com/randombit/botan/issues/3812 . Related CLI test
+ * test_sym_encrypted__rnp_aead_botan_crash */
+
+ auto data = file_to_vec("data/test_messages/message.aead-windows-issue-botan");
+ /* First 32 bytes are encrypted key as it was extracted from the OpenPGP stream, so
+ * skipping. */
+ uint8_t *idx = data.data() + 32;
+ uint8_t bufbin[64] = {0};
+ uint8_t outbuf[32768] = {0};
+ size_t outsz = sizeof(outbuf);
+ size_t written = 0;
+ size_t read = 0;
+ size_t diff = 0;
+
+ /* Now the data which exposes a possible crash */
+ struct botan_cipher_struct *cipher = NULL;
+ assert_int_equal(botan_cipher_init(&cipher, "AES-128/OCB", BOTAN_CIPHER_INIT_FLAG_DECRYPT),
+ 0);
+
+ const char *key2 = "417835a476bc5958b18d41fb00cf682d";
+ assert_int_equal(rnp::hex_decode(key2, bufbin, 16), 16);
+ assert_int_equal(botan_cipher_set_key(cipher, bufbin, 16), 0);
+
+ const char *ad2 = "d40107020c0000000000000000";
+ assert_int_equal(rnp::hex_decode(ad2, bufbin, 13), 13);
+ assert_int_equal(botan_cipher_set_associated_data(cipher, bufbin, 13), 0);
+
+ const char *nonce2 = "005dbbbe0088f9d17ca2d8d464920f";
+ assert_int_equal(rnp::hex_decode(nonce2, bufbin, 15), 15);
+ assert_int_equal(botan_cipher_start(cipher, bufbin, 15), 0);
+
+ assert_int_equal(
+ botan_cipher_update(cipher, 0, outbuf, outsz, &written, idx, 32736, &read), 0);
+ diff = 32736 - read;
+ idx += read;
+
+ assert_int_equal(
+ botan_cipher_update(cipher, 0, outbuf, outsz, &written, idx, diff + 32736, &read), 0);
+ idx += read;
+ diff = diff + 32736 - read;
+
+ assert_int_equal(
+ botan_cipher_update(cipher, 0, outbuf, outsz, &written, idx, diff + 32736, &read), 0);
+ idx += read;
+ diff = diff + 32736 - read;
+
+ assert_int_equal(
+ botan_cipher_update(cipher, 0, outbuf, outsz, &written, idx, diff + 32736, &read), 0);
+ idx += read;
+ diff = diff + 32736 - read;
+
+ uint32_t ver_major = botan_version_major();
+ uint32_t ver_minor = botan_version_minor();
+ uint32_t ver_patch = botan_version_patch();
+ uint32_t ver = (ver_major << 16) | (ver_minor << 8) | ver_patch;
+ uint32_t ver_2_19_3 = (2 << 16) | (19 << 8) | 3;
+ uint32_t ver_3_2_0 = (3 << 16) | (2 << 8);
+ bool check = true;
+ /* Currently AV happens with versions up to 2.19.3 and 3.2.0 */
+ if ((ver_major == 2) && (ver <= ver_2_19_3)) {
+ check = false;
+ }
+ if ((ver_major == 3) && (ver <= ver_3_2_0)) {
+ check = false;
+ }
+
+ if (check) {
+ assert_int_equal(botan_cipher_update(cipher,
+ BOTAN_CIPHER_UPDATE_FLAG_FINAL,
+ outbuf,
+ outsz,
+ &written,
+ idx,
+ diff + 25119,
+ &read),
+ 0);
+ }
+
+ assert_int_equal(botan_cipher_reset(cipher), 0);
+ assert_int_equal(botan_cipher_destroy(cipher), 0);
+}
+#endif
diff --git a/src/tests/cipher_cxx.cpp b/src/tests/cipher_cxx.cpp
index b5f7f83..a33e38e 100644
--- a/src/tests/cipher_cxx.cpp
+++ b/src/tests/cipher_cxx.cpp
@@ -138,7 +138,7 @@ test_cipher(pgp_symm_alg_t alg,
std::vector<uint8_t> decrypted(ct.size());
// all except the last block but see below for openssl
nonfinal_bytes = rnp_round_up(ct.size(), ud) - ud;
-#ifdef CRYPTO_BACKEND_OPENSSL
+#if defined(CRYPTO_BACKEND_OPENSSL) || defined(CRYPTO_BACKEND_BOTAN3)
/* Since ossl backend sets tag explicitly tag bytes cannot be
split between two blocks.
The issue may easily occur is (for example)
@@ -146,6 +146,7 @@ test_cipher(pgp_symm_alg_t alg,
ct.size() = 24
tag_size=16
*/
+ /* Botan 3 also requires to include whole tag in the finish() call. */
if (ct.size() - nonfinal_bytes < tag_size) {
nonfinal_bytes = ct.size() - tag_size;
}
@@ -153,12 +154,16 @@ test_cipher(pgp_symm_alg_t alg,
output_written = 0;
input_consumed = 0;
while (input_consumed != nonfinal_bytes) {
+ size_t consume = std::min(ud, nonfinal_bytes - input_consumed);
+ if (consume < ud) {
+ break;
+ }
assert_true(dec->update(decrypted.data() + output_written,
decrypted.size() - output_written,
&written,
(const uint8_t *) ct.data() + input_consumed,
// ++++ ud,
- std::min(ud, nonfinal_bytes - input_consumed),
+ consume,
&consumed));
output_written += written;
input_consumed += consumed;
diff --git a/src/tests/cli_common.py b/src/tests/cli_common.py
index 12bf5d8..f8d7001 100644
--- a/src/tests/cli_common.py
+++ b/src/tests/cli_common.py
@@ -1,10 +1,10 @@
import sys
-import distutils.spawn
import random
import string
import logging
import os
import re
+import shutil
from subprocess import Popen, PIPE
RNP_ROOT = None
@@ -78,7 +78,7 @@ def file_text(path, encoding = CONSOLE_ENCODING):
return f.read().decode(encoding).replace('\r\r', '\r')
def find_utility(name, exitifnone = True):
- path = distutils.spawn.find_executable(name)
+ path = shutil.which(name)
if not path and exitifnone:
logging.error('Cannot find utility {}. Exiting.'.format(name))
sys.exit(1)
diff --git a/src/tests/cli_tests.py b/src/tests/cli_tests.py
index e6f5ed7..47f6890 100755
--- a/src/tests/cli_tests.py
+++ b/src/tests/cli_tests.py
@@ -9,6 +9,7 @@ import sys
import tempfile
import time
import unittest
+import random
from platform import architecture
from cli_common import (file_text, find_utility, is_windows, list_upto,
@@ -47,6 +48,8 @@ RNP_IDEA = True
RNP_BLOWFISH = True
RNP_CAST5 = True
RNP_RIPEMD160 = True
+# Botan may cause AV during OCB decryption in certain cases, see https://github.com/randombit/botan/issues/3812
+RNP_BOTAN_OCB_AV = False
if sys.version_info >= (3,):
unichr = chr
@@ -861,6 +864,7 @@ def gpg_check_features():
def rnp_check_features():
global RNP_TWOFISH, RNP_BRAINPOOL, RNP_AEAD, RNP_AEAD_EAX, RNP_AEAD_OCB, RNP_AEAD_OCB_AES, RNP_IDEA, RNP_BLOWFISH, RNP_CAST5, RNP_RIPEMD160
+ global RNP_BOTAN_OCB_AV
ret, out, _ = run_proc(RNP, ['--version'])
if ret != 0:
raise_err('Failed to get RNP version.')
@@ -869,6 +873,14 @@ def rnp_check_features():
RNP_AEAD_OCB = re.match(r'(?s)^.*AEAD:.*OCB.*', out) is not None
RNP_AEAD = RNP_AEAD_EAX or RNP_AEAD_OCB
RNP_AEAD_OCB_AES = RNP_AEAD_OCB and re.match(r'(?s)^.*Backend.*OpenSSL.*', out) is not None
+ # Botan OCB crash
+ if re.match(r'(?s)^.*Backend.*Botan.*', out):
+ match = re.match(r'(?s)^.*Backend version: ([\d]+)\.([\d]+)\.([\d]+).*$', out)
+ ver = [int(match.group(1)), int(match.group(2)), int(match.group(3))]
+ if ver <= [2, 19, 3]:
+ RNP_BOTAN_OCB_AV = True
+ if (ver >= [3, 0, 0]) and (ver <= [3, 2, 0]):
+ RNP_BOTAN_OCB_AV = True
# Twofish
RNP_TWOFISH = re.match(r'(?s)^.*Encryption:.*TWOFISH.*', out) is not None
# Brainpool curves
@@ -887,6 +899,7 @@ def rnp_check_features():
print('RNP_AEAD_EAX: ' + str(RNP_AEAD_EAX))
print('RNP_AEAD_OCB: ' + str(RNP_AEAD_OCB))
print('RNP_AEAD_OCB_AES: ' + str(RNP_AEAD_OCB_AES))
+ print('RNP_BOTAN_OCB_AV: ' + str(RNP_BOTAN_OCB_AV))
def setup(loglvl):
# Setting up directories.
@@ -3037,10 +3050,10 @@ class Misc(unittest.TestCase):
def test_backend_version(self):
BOTAN_BACKEND_VERSION = r'(?s)^.*.' \
'Backend: Botan.*' \
- 'Backend version: ([a-zA-z\.0-9]+).*$'
+ 'Backend version: ([a-zA-Z\\.0-9]+).*$'
OPENSSL_BACKEND_VERSION = r'(?s)^.*' \
'Backend: OpenSSL.*' \
- 'Backend version: ([a-zA-z\.0-9]+).*$'
+ 'Backend version: ([a-zA-Z\\.0-9]+).*$'
# Run without parameters and make sure it matches
ret, out, _ = run_proc(RNP, [])
self.assertNotEqual(ret, 0)
@@ -3054,28 +3067,33 @@ class Misc(unittest.TestCase):
if not match:
match = re.match(OPENSSL_BACKEND_VERSION, out)
backend_prog = 'openssl'
- openssl_root = os.getenv('OPENSSL_ROOT_DIR')
+ openssl_root = os.getenv('RNP_TESTS_OPENSSL_ROOT')
else:
openssl_root = None
self.assertTrue(match)
# check there is no unexpected output
self.assertNotRegex(err, r'(?is)^.*Unsupported.*$')
self.assertNotRegex(err, r'(?is)^.*pgp_sa_to_openssl_string.*$')
+ self.assertNotRegex(err, r'(?is)^.*unknown.*$')
# In case when there are several openssl installations
# testing environment is supposed to point to the right one
# through OPENSSL_ROOT_DIR environment variable
+ if is_windows():
+ backend_prog += '.exe'
+ backend_prog_ext = None
if openssl_root is not None:
- backen_prog_ext = shutil.which(backend_prog, path = openssl_root + '/bin')
+ backend_prog_ext = shutil.which(backend_prog, path = openssl_root + '/bin')
else:
# In all other cases
# check that botan or openssl executable binary exists in PATH
- backen_prog_ext = shutil.which(backend_prog)
+ backend_prog_ext = shutil.which(backend_prog)
- if backen_prog_ext is not None:
- ret, out, _ = run_proc(backen_prog_ext, ['version'])
- self.assertEqual(ret, 0)
- self.assertIn(match.group(1), out)
+ if backend_prog_ext is None:
+ return
+ ret, out, _ = run_proc(backend_prog_ext, ['version'])
+ self.assertEqual(ret, 0)
+ self.assertIn(match.group(1), out)
def test_help_message(self):
# rnp help message
@@ -3971,6 +3989,17 @@ class Misc(unittest.TestCase):
clear_workfiles()
shutil.rmtree(RNP2, ignore_errors=True)
+ def test_armored_detection_on_cleartext(self):
+ ret, out, err = run_proc(RNP, ['--keyfile', data_path(SECRING_1), '--password', PASSWORD, '--clearsign'], 'Hello\n')
+ self.assertEqual(ret, 0)
+ self.assertRegex(out, r'(?s)^.*BEGIN PGP SIGNED MESSAGE.*$')
+ self.assertRegex(out, r'(?s)^.*BEGIN PGP SIGNATURE.*$')
+ ret, _, err = run_proc(RNP, ['--keyfile', data_path(PUBRING_1), '--verify', '-'], out)
+ self.assertEqual(ret, 0)
+ self.assertRegex(err, r'(?s)^.*Good signature made.*$')
+ self.assertNotRegex(err, r'(?s)^.*Warning: missing or malformed CRC line.*$')
+ self.assertNotRegex(err, r'(?s)^.*wrong armor trailer.*$')
+
class Encryption(unittest.TestCase):
'''
Things to try later:
@@ -4120,12 +4149,25 @@ class Encryption(unittest.TestCase):
AEAD_C = list_upto(CIPHERS, Encryption.RUNS)
AEAD_M = list_upto(AEADS, Encryption.RUNS)
AEAD_B = list_upto([None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16], Encryption.RUNS)
+ SIZES = Encryption.SIZES_R
+ random.shuffle(SIZES)
# Encrypt and decrypt cleartext using the AEAD
- for size, cipher, aead, bits, z in zip(Encryption.SIZES_R, AEAD_C,
+ for size, cipher, aead, bits, z in zip(SIZES, AEAD_C,
AEAD_M, AEAD_B, Encryption.Z_R):
+ if RNP_BOTAN_OCB_AV and (aead == 'ocb') and (size > 30000):
+ continue
rnp_sym_encryption_rnp_aead(size, cipher, z, [aead, bits], GPG_AEAD)
+ def test_sym_encrypted__rnp_aead_botan_crash(self):
+ if RNP_BOTAN_OCB_AV:
+ return
+ dst, = reg_workfiles('cleartext', '.txt')
+ rnp_decrypt_file(data_path('test_messages/message.aead-windows-issue'), dst)
+ remove_files(dst)
+ rnp_decrypt_file(data_path('test_messages/message.aead-windows-issue2'), dst)
+ remove_files(dst)
+
def test_aead_chunk_edge_cases(self):
if not RNP_AEAD:
print('AEAD is not available for RNP - skipping.')
@@ -4710,12 +4752,16 @@ class EncryptElgamal(Encrypt):
self.operation_key_location = tuple((key_path(pfx, False), key_path(pfx, True)))
self.rnp.userid = self.gpg.userid = pfx + AT_EXAMPLE
# DSA 1024 key uses SHA-1 as hash but verification would succeed till 2024
+ if sign_key_size == 1024:
+ return
self._encrypt_decrypt(self.gpg, self.rnp)
def do_test_decrypt(self, sign_key_size, enc_key_size):
pfx = EncryptElgamal.key_pfx(sign_key_size, enc_key_size)
self.operation_key_location = tuple((key_path(pfx, False), key_path(pfx, True)))
self.rnp.userid = self.gpg.userid = pfx + AT_EXAMPLE
+ if sign_key_size == 1024:
+ return
self._encrypt_decrypt(self.rnp, self.gpg)
def test_encrypt_P1024_1024(self): self.do_test_encrypt(1024, 1024)
@@ -4726,11 +4772,7 @@ class EncryptElgamal(Encrypt):
def test_decrypt_P2048_2048(self): self.do_test_decrypt(2048, 2048)
def test_decrypt_P1234_1234(self): self.do_test_decrypt(1234, 1234)
- def test_generate_elgamal_key1024_in_gpg_and_encrypt(self):
- cmd = EncryptElgamal.GPG_GENERATE_DSA_ELGAMAL_PATTERN.format(1024, 1024, self.gpg.userid)
- self.operation_key_gencmd = cmd
- # Will not fail till 2024 since 1024-bit DSA key uses SHA-1 as hash.
- self._encrypt_decrypt(self.gpg, self.rnp)
+ # 1024-bit key generation test was removed since it uses SHA1, which is not allowed for key signatures since Jan 19, 2024.
def test_generate_elgamal_key1536_in_gpg_and_encrypt(self):
cmd = EncryptElgamal.GPG_GENERATE_DSA_ELGAMAL_PATTERN.format(1536, 1536, self.gpg.userid)
diff --git a/src/tests/data/test_messages/message.aead-windows-issue b/src/tests/data/test_messages/message.aead-windows-issue
new file mode 100644
index 0000000..4836610
--- /dev/null
+++ b/src/tests/data/test_messages/message.aead-windows-issue
Binary files differ
diff --git a/src/tests/data/test_messages/message.aead-windows-issue-botan b/src/tests/data/test_messages/message.aead-windows-issue-botan
new file mode 100644
index 0000000..84e17e8
--- /dev/null
+++ b/src/tests/data/test_messages/message.aead-windows-issue-botan
Binary files differ
diff --git a/src/tests/data/test_messages/message.aead-windows-issue2 b/src/tests/data/test_messages/message.aead-windows-issue2
new file mode 100644
index 0000000..9af3d50
--- /dev/null
+++ b/src/tests/data/test_messages/message.aead-windows-issue2
Binary files differ
diff --git a/src/tests/data/test_messages/message.txt.signed-mimemode b/src/tests/data/test_messages/message.txt.signed-mimemode
new file mode 100644
index 0000000..27f7579
--- /dev/null
+++ b/src/tests/data/test_messages/message.txt.signed-mimemode
Binary files differ
diff --git a/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line-2.asc b/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line-2.asc
new file mode 100644
index 0000000..dbec69f
--- /dev/null
+++ b/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line-2.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEWsN6MBYJKwYBBAHaRw8BAQdAAS+nkv9BdVi0JX7g6d+O201bdKhdowbielOo
+ugCpCfi0CWVjYy0yNTUxOYiUBBMWCAA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgID
+AQIeAwIXgBYhBCH8aCdKrjtd45pCd8x4YniYGwcoBQJcVa/NAAoJEMx4YniYGwco
+lFAA/jMt3RUUb5xt63JW6HFcrYq0RrDAcYMsXAY73iZpPsEcAQDmKbH21LkwoClU
+9RrUJSYZnMla/pQdgOxd7/PjRCpbCg==
+=miZp
+
+
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line.asc b/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line.asc
new file mode 100644
index 0000000..9617fb9
--- /dev/null
+++ b/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEWsN6MBYJKwYBBAHaRw8BAQdAAS+nkv9BdVi0JX7g6d+O201bdKhdowbielOo
+ugCpCfi0CWVjYy0yNTUxOYiUBBMWCAA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgID
+AQIeAwIXgBYhBCH8aCdKrjtd45pCd8x4YniYGwcoBQJcVa/NAAoJEMx4YniYGwco
+lFAA/jMt3RUUb5xt63JW6HFcrYq0RrDAcYMsXAY73iZpPsEcAQDmKbH21LkwoClU
+9RrUJSYZnMla/pQdgOxd7/PjRCpbCg==
+=miZp
+
+
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp
index 55b0d10..40b17cc 100644
--- a/src/tests/ffi-enc.cpp
+++ b/src/tests/ffi-enc.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2020-2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -178,15 +178,16 @@ TEST_F(rnp_tests, test_ffi_encrypt_pass)
assert_rnp_failure(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH"));
assert_rnp_failure(
rnp_op_encrypt_add_password(op, "pass2", "SHA256", 12345, "TWOFISH"));
- assert_rnp_success(
- rnp_op_encrypt_add_password(op, "pass2", "SHA256", 12345, "BLOWFISH"));
+ const char *alg = blowfish_enabled() ? "BLOWFISH" : "AES256";
+ assert_rnp_success(rnp_op_encrypt_add_password(op, "pass2", "SHA256", 12345, alg));
} else if (!sm2_enabled() && twofish_enabled()) {
assert_rnp_failure(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH"));
assert_rnp_success(
rnp_op_encrypt_add_password(op, "pass2", "SHA256", 12345, "TWOFISH"));
} else if (sm2_enabled() && !twofish_enabled()) {
assert_rnp_failure(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH"));
- assert_rnp_success(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "BLOWFISH"));
+ const char *alg = blowfish_enabled() ? "BLOWFISH" : "AES256";
+ assert_rnp_success(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, alg));
} else {
assert_rnp_success(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH"));
}
@@ -598,7 +599,7 @@ TEST_F(rnp_tests, test_ffi_encrypt_pk)
rnp_ffi_destroy(ffi);
}
-bool
+static bool
first_key_password_provider(rnp_ffi_t ffi,
void * app_ctx,
rnp_key_handle_t key,
@@ -1360,3 +1361,29 @@ TEST_F(rnp_tests, test_ffi_encrypt_no_wrap)
// final cleanup
rnp_ffi_destroy(ffi);
}
+
+TEST_F(rnp_tests, test_ffi_mimemode_signature)
+{
+ rnp_ffi_t ffi = NULL;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ assert_true(import_pub_keys(ffi, "data/test_stream_key_load/ecc-25519-pub.asc"));
+
+ rnp_input_t input = NULL;
+ assert_rnp_success(
+ rnp_input_from_path(&input, "data/test_messages/message.txt.signed-mimemode"));
+ rnp_output_t output = NULL;
+ assert_rnp_success(rnp_output_to_null(&output));
+ rnp_op_verify_t verify = NULL;
+ assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output));
+ assert_rnp_success(rnp_op_verify_execute(verify));
+ size_t sigcount = 255;
+ assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount));
+ assert_int_equal(sigcount, 1);
+ rnp_op_verify_signature_t sig = NULL;
+ assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig));
+ assert_rnp_success(rnp_op_verify_signature_get_status(sig));
+ rnp_op_verify_destroy(verify);
+ rnp_input_destroy(input);
+ rnp_output_destroy(output);
+ rnp_ffi_destroy(ffi);
+}
diff --git a/src/tests/ffi-key-sig.cpp b/src/tests/ffi-key-sig.cpp
index 01bfdd2..1176de5 100644
--- a/src/tests/ffi-key-sig.cpp
+++ b/src/tests/ffi-key-sig.cpp
@@ -902,6 +902,11 @@ TEST_F(rnp_tests, test_ffi_sig_validity)
uint32_t expires = 0;
assert_rnp_success(rnp_signature_get_expiration(sig, &expires));
assert_int_equal(expires, 86400);
+ uint32_t features = 0;
+ assert_rnp_failure(rnp_signature_get_features(NULL, &features));
+ assert_rnp_failure(rnp_signature_get_features(sig, NULL));
+ assert_rnp_success(rnp_signature_get_features(sig, &features));
+ assert_int_equal(features, 0);
rnp_signature_handle_destroy(sig);
rnp_uid_handle_destroy(uid);
rnp_key_handle_destroy(key);
diff --git a/src/tests/ffi-key.cpp b/src/tests/ffi-key.cpp
index 2933d68..a25e6c4 100644
--- a/src/tests/ffi-key.cpp
+++ b/src/tests/ffi-key.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2022-2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -4440,3 +4440,28 @@ TEST_F(rnp_tests, test_reprotect_keys)
assert_rnp_success(rnp_identifier_iterator_destroy(it));
rnp_ffi_destroy(ffi);
}
+
+TEST_F(rnp_tests, test_armored_keys_extra_line)
+{
+ rnp_ffi_t ffi = NULL;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ /* Key with extra line after the checksum */
+ assert_true(
+ import_pub_keys(ffi, "data/test_stream_key_load/ecc-25519-pub-extra-line.asc"));
+ rnp_key_handle_t key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "cc786278981b0728", &key));
+ assert_true(check_key_valid(key, true));
+ assert_true(check_uid_valid(key, 0, true));
+ rnp_key_handle_destroy(key);
+
+ /* Key with extra lines with spaces after the checksum */
+ assert_true(
+ import_pub_keys(ffi, "data/test_stream_key_load/ecc-25519-pub-extra-line-2.asc"));
+ key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "cc786278981b0728", &key));
+ assert_true(check_key_valid(key, true));
+ assert_true(check_uid_valid(key, 0, true));
+ rnp_key_handle_destroy(key);
+
+ rnp_ffi_destroy(ffi);
+}
diff --git a/src/tests/ffi.cpp b/src/tests/ffi.cpp
index 1e75871..4c9f553 100644
--- a/src/tests/ffi.cpp
+++ b/src/tests/ffi.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2020 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -29,6 +29,7 @@
#include <string>
#include <set>
#include <utility>
+#include <cstdint>
#include <rnp/rnp.h>
#include "rnp_tests.h"
@@ -2659,7 +2660,8 @@ TEST_F(rnp_tests, test_ffi_revocations)
assert_rnp_failure(rnp_uid_is_revoked(uid_handle, NULL));
assert_rnp_success(rnp_uid_is_revoked(uid_handle, &revoked));
assert_false(revoked);
- rnp_signature_handle_t sig = (rnp_signature_handle_t) 0xdeadbeef;
+ const uintptr_t p_sig = 0xdeadbeef;
+ rnp_signature_handle_t sig = reinterpret_cast<rnp_signature_handle_t>(p_sig);
assert_rnp_failure(rnp_uid_get_revocation_signature(NULL, &sig));
assert_rnp_failure(rnp_uid_get_revocation_signature(uid_handle, NULL));
assert_rnp_success(rnp_uid_get_revocation_signature(uid_handle, &sig));
@@ -5951,11 +5953,16 @@ TEST_F(rnp_tests, test_ffi_security_profile)
assert_int_equal(flags, 0);
/* SHA1 - now, data verify disabled, key sig verify is enabled */
flags = 0;
- assert_rnp_success(rnp_get_security_rule(
- ffi, RNP_FEATURE_HASH_ALG, "SHA1", time(NULL), &flags, &from, &level));
- assert_int_equal(from, SHA1_DATA_FROM);
+ auto now = time(NULL);
+ bool sha1_cutoff = now > SHA1_KEY_FROM;
+ /* This would pick default rule closer to the date independent on usage */
+ assert_rnp_success(
+ rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, "SHA1", now, &flags, &from, &level));
+ auto expect_from = sha1_cutoff ? SHA1_KEY_FROM : SHA1_DATA_FROM;
+ auto expect_usage = sha1_cutoff ? RNP_SECURITY_VERIFY_KEY : RNP_SECURITY_VERIFY_DATA;
+ assert_int_equal(from, expect_from);
assert_int_equal(level, RNP_SECURITY_INSECURE);
- assert_int_equal(flags, RNP_SECURITY_VERIFY_DATA);
+ assert_int_equal(flags, expect_usage);
flags = 0;
assert_rnp_success(rnp_get_security_rule(
ffi, RNP_FEATURE_HASH_ALG, "SHA1", SHA1_DATA_FROM - 1, &flags, &from, &level));
@@ -5968,11 +5975,14 @@ TEST_F(rnp_tests, test_ffi_security_profile)
assert_int_equal(level, RNP_SECURITY_INSECURE);
assert_int_equal(flags, RNP_SECURITY_VERIFY_DATA);
flags = RNP_SECURITY_VERIFY_KEY;
- assert_rnp_success(rnp_get_security_rule(
- ffi, RNP_FEATURE_HASH_ALG, "SHA1", time(NULL), &flags, &from, &level));
- assert_int_equal(from, 0);
- assert_int_equal(level, RNP_SECURITY_DEFAULT);
- assert_int_equal(flags, 0);
+ assert_rnp_success(
+ rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, "SHA1", now, &flags, &from, &level));
+ expect_from = sha1_cutoff ? SHA1_KEY_FROM : 0;
+ auto expect_level = sha1_cutoff ? RNP_SECURITY_INSECURE : RNP_SECURITY_DEFAULT;
+ expect_usage = sha1_cutoff ? RNP_SECURITY_VERIFY_KEY : 0;
+ assert_int_equal(from, expect_from);
+ assert_int_equal(level, expect_level);
+ assert_int_equal(flags, expect_usage);
flags = RNP_SECURITY_VERIFY_KEY;
assert_rnp_success(rnp_get_security_rule(
ffi, RNP_FEATURE_HASH_ALG, "SHA1", SHA1_KEY_FROM + 5, &flags, &from, &level));
diff --git a/src/tests/generatekey.cpp b/src/tests/generatekey.cpp
index b846ebb..dd0aaff 100644
--- a/src/tests/generatekey.cpp
+++ b/src/tests/generatekey.cpp
@@ -96,12 +96,11 @@ hash_supported(const std::string &hash)
}
static bool
-hash_secure(rnp_ffi_t ffi, const std::string &hash, uint32_t action)
+hash_secure(rnp_ffi_t ffi, const std::string &hash, uint32_t action, uint64_t time)
{
uint32_t flags = action;
uint32_t level = 0;
- rnp_get_security_rule(
- ffi, RNP_FEATURE_HASH_ALG, hash.c_str(), global_ctx.time(), &flags, NULL, &level);
+ rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, hash.c_str(), time, &flags, NULL, &level);
return level == RNP_SECURITY_DEFAULT;
}
@@ -185,7 +184,8 @@ TEST_F(rnp_tests, rnpkeys_generatekey_testSignature)
cfg.set_bool(CFG_OVERWRITE, true);
cfg.set_str(CFG_INFILE, "dummyfile.dat.pgp");
cfg.set_str(CFG_OUTFILE, "dummyfile.verify");
- if (!hash_secure(rnp.ffi, hashAlg[i], RNP_SECURITY_VERIFY_DATA)) {
+ if (!hash_secure(
+ rnp.ffi, hashAlg[i], RNP_SECURITY_VERIFY_DATA, global_ctx.time())) {
assert_false(cli_rnp_process_file(&rnp));
rnp.end();
assert_int_equal(rnp_unlink("dummyfile.dat.pgp"), 0);
@@ -361,7 +361,7 @@ TEST_F(rnp_tests, rnpkeys_generatekey_verifySupportedHashAlg)
assert_true(keycount > 0);
rnp_key_handle_t handle = NULL;
assert_rnp_success(rnp_locate_key(rnp.ffi, "userid", hashAlg[i], &handle));
- if (hash_secure(rnp.ffi, hashAlg[i], RNP_SECURITY_VERIFY_KEY)) {
+ if (hash_secure(rnp.ffi, hashAlg[i], RNP_SECURITY_VERIFY_KEY, global_ctx.time())) {
assert_non_null(handle);
bool valid = false;
rnp_key_is_valid(handle, &valid);
diff --git a/src/tests/key-add-userid.cpp b/src/tests/key-add-userid.cpp
index b80dbb6..fba6ec0 100644
--- a/src/tests/key-add-userid.cpp
+++ b/src/tests/key-add-userid.cpp
@@ -69,6 +69,8 @@ TEST_F(rnp_tests, test_key_add_userid)
selfsig0.key_flags = 0x2;
selfsig0.key_expiration = base_expiry;
selfsig0.primary = false;
+ auto curtime = global_ctx.time();
+ global_ctx.set_time(curtime > SHA1_KEY_FROM ? SHA1_KEY_FROM - 100 : 0);
key->add_uid_cert(selfsig0, PGP_HASH_SHA1, global_ctx);
// attempt to add sha1-signed uid and make sure it succeeds now and fails after the cutoff
// date in 2024
diff --git a/src/tests/load-pgp.cpp b/src/tests/load-pgp.cpp
index 560ed3d..6583eee 100644
--- a/src/tests/load-pgp.cpp
+++ b/src/tests/load-pgp.cpp
@@ -124,9 +124,10 @@ TEST_F(rnp_tests, test_load_v4_keyring_pgp)
/* Just a helper for the below test */
static void
-check_pgp_keyring_counts(const char * path,
- unsigned primary_count,
- const unsigned subkey_counts[])
+check_pgp_keyring_counts(const char * path,
+ unsigned primary_count,
+ const unsigned subkey_counts[],
+ rnp::SecurityContext &global_ctx)
{
pgp_source_t src = {};
rnp_key_store_t *key_store = new rnp_key_store_t(global_ctx);
@@ -175,10 +176,12 @@ TEST_F(rnp_tests, test_load_keyring_and_count_pgp)
unsigned int subkey_counts[2] = {3, 2};
// check pubring
- check_pgp_keyring_counts("data/keyrings/1/pubring.gpg", primary_count, subkey_counts);
+ check_pgp_keyring_counts(
+ "data/keyrings/1/pubring.gpg", primary_count, subkey_counts, global_ctx);
// check secring
- check_pgp_keyring_counts("data/keyrings/1/secring.gpg", primary_count, subkey_counts);
+ check_pgp_keyring_counts(
+ "data/keyrings/1/secring.gpg", primary_count, subkey_counts, global_ctx);
}
/* This test loads a V4 keyring and confirms that certain
diff --git a/src/tests/rnp_tests.cpp b/src/tests/rnp_tests.cpp
index 910d2d8..6961da4 100644
--- a/src/tests/rnp_tests.cpp
+++ b/src/tests/rnp_tests.cpp
@@ -35,11 +35,6 @@
static char original_dir[PATH_MAX];
-/*
- * Handler used to access DRBG.
- */
-rnp::SecurityContext global_ctx;
-
#ifdef _WIN32
void
rnpInvalidParameterHandler(const wchar_t *expression,
diff --git a/src/tests/rnp_tests.h b/src/tests/rnp_tests.h
index 2dd43e9..58f99c8 100644
--- a/src/tests/rnp_tests.h
+++ b/src/tests/rnp_tests.h
@@ -38,7 +38,8 @@ class rnp_tests : public ::testing::Test {
const char *original_dir() const;
protected:
- char *m_dir;
+ char * m_dir;
+ rnp::SecurityContext global_ctx;
};
typedef struct {
diff --git a/src/tests/streams.cpp b/src/tests/streams.cpp
index c0bf698..3219387 100644
--- a/src/tests/streams.cpp
+++ b/src/tests/streams.cpp
@@ -1086,7 +1086,7 @@ TEST_F(rnp_tests, test_stream_key_signatures)
}
static bool
-validate_key_sigs(const char *path)
+validate_key_sigs(const char *path, rnp::SecurityContext &global_ctx)
{
rnp_key_store_t *pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, path, global_ctx);
bool valid = rnp_key_store_load_from_path(pubring, NULL);
@@ -1144,32 +1144,30 @@ TEST_F(rnp_tests, test_stream_key_signature_validate)
delete pubring;
/* misc key files */
- assert_true(validate_key_sigs("data/test_stream_key_load/dsa-eg-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/dsa-eg-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-25519-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-25519-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-x25519-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-x25519-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p256-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p256-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p384-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p384-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p521-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p521-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp256-pub.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp256-sec.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp384-pub.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp384-sec.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp512-pub.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp512-sec.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p256k1-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p256k1-sec.asc"));
+ auto validate = [this](const std::string &file) {
+ auto path = "data/test_stream_key_load/" + file;
+ return validate_key_sigs(path.c_str(), this->global_ctx);
+ };
+ assert_true(validate("dsa-eg-pub.asc"));
+ assert_true(validate("dsa-eg-sec.asc"));
+ assert_true(validate("ecc-25519-pub.asc"));
+ assert_true(validate("ecc-25519-sec.asc"));
+ assert_true(validate("ecc-x25519-pub.asc"));
+ assert_true(validate("ecc-x25519-sec.asc"));
+ assert_true(validate("ecc-p256-pub.asc"));
+ assert_true(validate("ecc-p256-sec.asc"));
+ assert_true(validate("ecc-p384-pub.asc"));
+ assert_true(validate("ecc-p384-sec.asc"));
+ assert_true(validate("ecc-p521-pub.asc"));
+ assert_true(validate("ecc-p521-sec.asc"));
+ assert_true(validate("ecc-bp256-pub.asc") == brainpool_enabled());
+ assert_true(validate("ecc-bp256-sec.asc") == brainpool_enabled());
+ assert_true(validate("ecc-bp384-pub.asc") == brainpool_enabled());
+ assert_true(validate("ecc-bp384-sec.asc") == brainpool_enabled());
+ assert_true(validate("ecc-bp512-pub.asc") == brainpool_enabled());
+ assert_true(validate("ecc-bp512-sec.asc") == brainpool_enabled());
+ assert_true(validate("ecc-p256k1-pub.asc"));
+ assert_true(validate("ecc-p256k1-sec.asc"));
}
TEST_F(rnp_tests, test_stream_verify_no_key)
@@ -1568,11 +1566,15 @@ TEST_F(rnp_tests, test_stream_dearmor_edge_cases)
len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n%s\n", HDR, b64, CRC, FTR2);
assert_false(try_dearmor(msg, len));
- /* extra spaces or chars before the footer - FAIL */
+ /* extra spaces or tabs before the footer - allow it, see issue #2199 */
len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n %s\n", HDR, b64, CRC, FTR);
- assert_false(try_dearmor(msg, len));
+ assert_true(try_dearmor(msg, len));
len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n\t\t %s\n", HDR, b64, CRC, FTR);
+ assert_true(try_dearmor(msg, len));
+ /* no empty line between crc and footer - FAIL */
+ len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s%s\n", HDR, b64, CRC, FTR);
assert_false(try_dearmor(msg, len));
+ /* extra chars before the footer - FAIL */
len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n11111%s\n", HDR, b64, CRC, FTR);
assert_false(try_dearmor(msg, len));
@@ -1606,7 +1608,8 @@ TEST_F(rnp_tests, test_stream_dearmor_edge_cases)
}
static void
-add_openpgp_layers(const char *msg, pgp_dest_t &pgpdst, int compr, int encr)
+add_openpgp_layers(
+ const char *msg, pgp_dest_t &pgpdst, int compr, int encr, rnp::SecurityContext &global_ctx)
{
pgp_source_t src = {};
pgp_dest_t dst = {};
@@ -1648,7 +1651,7 @@ TEST_F(rnp_tests, test_stream_deep_packet_nesting)
pgp_dest_t dst = {};
/* add 30 compression layers and 2 encryption - must fail */
- add_openpgp_layers(message, dst, 30, 2);
+ add_openpgp_layers(message, dst, 30, 2, global_ctx);
#ifdef DUMP_TEST_CASE
/* remove ifdef if you want to write it to stdout */
pgp_source_t src = {};
@@ -1676,7 +1679,7 @@ TEST_F(rnp_tests, test_stream_deep_packet_nesting)
dst_close(&dst, false);
/* add 27 compression & 4 encryption layers - must succeed */
- add_openpgp_layers("message", dst, 27, 4);
+ add_openpgp_layers("message", dst, 27, 4, global_ctx);
#ifdef DUMP_TEST_CASE
/* remove ifdef if you want to write it to stdout */
assert_rnp_success(init_mem_src(&src, mem_dest_get_memory(&dst), dst.writeb, false));
diff --git a/src/tests/support.cpp b/src/tests/support.cpp
index c94a901..2867304 100644
--- a/src/tests/support.cpp
+++ b/src/tests/support.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -184,6 +184,7 @@ path_mkdir(mode_t mode, const char *first, ...)
assert_int_equal(0, RNP_MKDIR(buffer, mode));
}
+#ifndef WINSHELLAPI
static int
remove_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
@@ -193,6 +194,7 @@ remove_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ft
return ret;
}
+#endif
static const char *
get_tmp()
@@ -737,7 +739,9 @@ check_json_field_bool(json_object *obj, const std::string &field, bool value)
if (!json_object_is_type(fld, json_type_boolean)) {
return false;
}
- return json_object_get_boolean(fld) == value;
+ // 'json_object_get_boolean' returns 'json_bool' which is 'int' on Windows
+ // but bool on other platforms
+ return (json_object_get_boolean(fld) ? true : false) == value;
}
bool
diff --git a/src/tests/support.h b/src/tests/support.h
index 6206924..543a0d4 100644
--- a/src/tests/support.h
+++ b/src/tests/support.h
@@ -69,8 +69,6 @@ char *mkdtemp(char *templ);
#define realpath(N, R) _fullpath((R), (N), _MAX_PATH)
#endif
-extern rnp::SecurityContext global_ctx;
-
off_t file_size(const char *path);
/* Read file contents into the std::string */
diff --git a/version.txt b/version.txt
index c5523bd..7cca771 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-0.17.0
+0.17.1