diff --git a/.cirrus.yml b/.cirrus.yml index 0d0115f972..2fd7d95a23 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 ad4e6c1f7e..6577da3b20 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 35f1ea5078..3e16728206 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 30991fc280..e5bcc979f7 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 81336ec33d..c51260a4c3 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 0cfea7e269..42aef3091c 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 e71ee512e2..c5bbcfdbe3 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 0000000000..d02381c96f --- /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 51fd0c36ee..a7931d1351 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 87c37ac873..afed7e7ca7 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 773a9bdece..cc6cdc5576 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 6cdbd78357..3fa14414e3 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 611e46122b..e17f8e214a 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 bb6d40cb41..ecf2191bb6 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 a0162faa63..bcfe062d85 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 similarity index 100% rename from ci/build_package_rpm.sh rename to ci-legacy/build_package_rpm.sh diff --git a/ci/env-common.inc.sh b/ci-legacy/env-common.inc.sh similarity index 90% rename from ci/env-common.inc.sh rename to ci-legacy/env-common.inc.sh index 11712f0831..1e2f9ed6e0 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=$( /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 892c9a63cc..0000000000 --- a/ci/docker/fedora35.test-locally +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -rm -rf /tmp/rnp* - -cat > run-this < rnp-version diff --git a/ci/tests/deb-tests.sh b/ci/tests/deb-tests.sh index 7805702677..356302238b 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 2b22df99f3..cd496b4e7b 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 dbeaac6d20..dcc4d0147f 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 39f8dd0302..75b676e98d 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 1a00177739..03f3c61796 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 0000000000..4362b542ae --- /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 2708491947..0000000000 --- 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 69677649d0..c3e3e33149 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 390f1d2473..ed7eb8eca1 100644 --- a/cmake/Modules/findopensslfeatures.c +++ b/cmake/Modules/findopensslfeatures.c @@ -4,6 +4,9 @@ #include #include #include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#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 2180845269..edea2a7c2e 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 0000000000..52dbce8864 --- /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 e0c0e4116e..28f0617951 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 991fa85291..6ca6856946 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 5cab632be3..d69e452ca5 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 66671698a2..149264da6d 100644 --- a/include/rnp/rnp.h +++ b/include/rnp/rnp.h @@ -138,6 +138,13 @@ typedef uint32_t rnp_result_t; #define RNP_VERIFY_REQUIRE_ALL_SIGS (1U << 1) #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 */ @@ -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 095c325bcb..b7e2b52d52 100644 --- a/src/common/uniwin.h +++ b/src/common/uniwin.h @@ -35,9 +35,9 @@ #include /* 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 bac4e1325a..ad16cd05ad 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 768e66911b..7c9ce6007f 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 35adeb7343..89bd01ad9e 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 cd6c849f63..7dcfb019b3 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 2afb59ab44..316e7a11bb 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 086ac57d8e..3b4b444098 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 "$") + list(REMOVE_ITEM val "$") 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 f8880d55d7..afb2cfe327 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 26346049e8..da3cba1c4f 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 859b048c36..32af839435 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 26cc547690..a4cfa1acd2 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 c9edf15789..f683b76401 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 c2c4ab3939..6f4a4fcf28 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 1e96218af9..4845baad82 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 #include #include +#if defined(CRYPTO_BACKEND_OPENSSL3) +#include +#include +#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 @@ dl_validate_secret_key(EVP_PKEY *dlkey, const pgp_mpi_t &mx) 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 1fb75b5fce..3f91b72a9a 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 #include #include +#if defined(CRYPTO_BACKEND_OPENSSL3) +#include +#include +#endif #define DSA_MAX_Q_BITLEN 256 @@ -83,66 +87,118 @@ dsa_encode_sig(uint8_t *data, size_t *len, const pgp_dsa_signature_t &sig) 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 @@ dsa_verify(const pgp_dsa_signature_t *sig, 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 6974b4c210..46c46771d2 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 #include #include +#if defined(CRYPTO_BACKEND_OPENSSL3) +#include +#include +#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 @@ ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve) 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 d4411c39f0..574b3b868d 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 60b7260456..566031870a 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 sec; rnp::secure_array 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 f3fa381fd8..6049ad2ff2 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 #include #include +#if defined(CRYPTO_BACKEND_OPENSSL3) +#include +#include +#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 @@ elgamal_generate(rnp::RNG *rng, pgp_eg_key_t *key, size_t keybits) 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 f7ddefe318..83fa0445b2 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 24cff296d9..445974c117 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 @@ rsa_load_public_key(const pgp_rsa_key_t *key) 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 @@ rsa_decrypt_pkcs1(rnp::RNG * rng, 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 aeed78432d..0f7b259913 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 #include #include +#include #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 98e90eded3..ff193620ac 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(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 24c46f986d..9e92aaf91d 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 @@ -6196,6 +6196,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 { @@ -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 b888680f50..b062c52f19 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 669c3057c3..b6669dc391 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 5ec4d64be9..f69f2221d6 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 6f3bc81fe1..ab4098f39b 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 4f36c381f1..943efa96ae 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/libsexp b/src/libsexp deleted file mode 160000 index ee796f53ad..0000000000 --- a/src/libsexp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ee796f53ad83a40f7b81e52e27ec9ca9a19707fa diff --git a/src/libsexpp b/src/libsexpp new file mode 160000 index 0000000000..e265f6370d --- /dev/null +++ b/src/libsexpp @@ -0,0 +1 @@ +Subproject commit e265f6370dd750b4f23ffbfcbb477514f7c2a517 diff --git a/src/rnp/fficli.cpp b/src/rnp/fficli.cpp index fa118eea23..b6e1b9a258 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::vectorresfp; @@ -2955,7 +2954,6 @@ cli_rnp_print_signatures(cli_rnp_t *rnp, const std::vector= 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 8bcb7e1115..3dd088cc5a 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 1a6997c288..6a7885d3bb 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 7d2a6b0cfe..d1a89d46bd 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 25b98bfa57..3df5f0b6b4 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 b5f7f833f9..a33e38e6e5 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 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 12bf5d817a..f8d7001fc8 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 e6f5ed76b8..47f68900fe 100755 --- a/src/tests/cli_tests.py +++ b/src/tests/cli_tests.py @@ -9,6 +9,7 @@ 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_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 @@ def test_core_dumps(self): 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 @@ def test_backend_version(self): 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 @@ def test_allow_weak_hash(self): 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 @@ def test_sym_encryption__rnp_aead(self): 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 @@ def do_test_encrypt(self, 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 # 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 @@ def test_decrypt_P1024_1024(self): self.do_test_decrypt(1024, 1024) 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 0000000000..4836610c27 Binary files /dev/null and b/src/tests/data/test_messages/message.aead-windows-issue 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 0000000000..84e17e8b34 Binary files /dev/null and b/src/tests/data/test_messages/message.aead-windows-issue-botan 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 0000000000..9af3d50c0b Binary files /dev/null and b/src/tests/data/test_messages/message.aead-windows-issue2 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 0000000000..27f75798f1 Binary files /dev/null and b/src/tests/data/test_messages/message.txt.signed-mimemode 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 0000000000..dbec69f826 --- /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 0000000000..9617fb90f3 --- /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 55b0d10b9c..40b17ccf39 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 01bfdd262b..1176de51fd 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 2933d6854e..a25e6c4bcc 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 1e75871559..4c9f5536be 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 #include #include +#include #include #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(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 b846ebb4be..dd0aaffa33 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 b80dbb6cf0..fba6ec08b6 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 560ed3df2f..6583eee2f8 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 910d2d8bb7..6961da4a0d 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 2dd43e98bb..58f99c8b55 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 c0bf698309..3219387f64 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 c94a901094..2867304d6f 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 6206924501..543a0d43b6 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 c5523bd09b..7cca7711a0 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.17.0 +0.17.1