summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 19:22:08 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 19:22:08 +0000
commit55fffa809312f6c0ae40528fd1ee66b7559c4650 (patch)
treebb6b3e63b35220d36bdc654f784a985e88ba1cd2 /src
parentReleasing progress-linux version 0.17.0-3~progress7.99u1. (diff)
downloadrnp-55fffa809312f6c0ae40528fd1ee66b7559c4650.tar.xz
rnp-55fffa809312f6c0ae40528fd1ee66b7559c4650.zip
Merging upstream version 0.17.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src')
-rw-r--r--src/common/uniwin.h4
-rw-r--r--src/fuzzing/keyring.c13
-rw-r--r--src/fuzzing/keyring_kbx.c12
-rw-r--r--src/fuzzing/sigimport.c11
-rw-r--r--src/fuzzing/verify.c13
-rw-r--r--src/fuzzing/verify_detached.c19
-rwxr-xr-xsrc/lib/CMakeLists.txt105
-rw-r--r--src/lib/config.h.in4
-rw-r--r--src/lib/crypto.cpp4
-rw-r--r--src/lib/crypto/backend_version.cpp12
-rw-r--r--src/lib/crypto/bn.h78
-rw-r--r--src/lib/crypto/cipher.hpp5
-rw-r--r--src/lib/crypto/cipher_botan.cpp8
-rw-r--r--src/lib/crypto/dl_ossl.cpp118
-rw-r--r--src/lib/crypto/dsa_ossl.cpp174
-rw-r--r--src/lib/crypto/ec_ossl.cpp181
-rw-r--r--src/lib/crypto/ecdh.cpp13
-rw-r--r--src/lib/crypto/ecdh_ossl.cpp56
-rw-r--r--src/lib/crypto/elgamal_ossl.cpp99
-rw-r--r--src/lib/crypto/rsa.cpp8
-rw-r--r--src/lib/crypto/rsa_ossl.cpp351
-rw-r--r--src/lib/crypto/symmetric.cpp12
-rw-r--r--src/lib/crypto/symmetric_ossl.cpp52
-rw-r--r--src/lib/rnp.cpp23
-rw-r--r--src/librekey/g23_sexp.hpp4
-rw-r--r--src/librepgp/stream-armor.cpp6
-rw-r--r--src/librepgp/stream-parse.cpp6
-rw-r--r--src/librepgp/stream-sig.cpp11
-rw-r--r--src/librepgp/stream-sig.h2
-rw-r--r--src/rnp/fficli.cpp2
-rw-r--r--src/rnp/rnp.cpp8
-rw-r--r--src/rnpkeys/main.cpp6
-rw-r--r--src/rnpkeys/rnpkeys.cpp6
-rw-r--r--src/tests/CMakeLists.txt29
-rw-r--r--src/tests/cipher.cpp96
-rw-r--r--src/tests/cipher_cxx.cpp9
-rw-r--r--src/tests/cli_common.py4
-rwxr-xr-xsrc/tests/cli_tests.py72
-rw-r--r--src/tests/data/test_messages/message.aead-windows-issuebin0 -> 156183 bytes
-rw-r--r--src/tests/data/test_messages/message.aead-windows-issue-botanbin0 -> 156111 bytes
-rw-r--r--src/tests/data/test_messages/message.aead-windows-issue2bin0 -> 193572 bytes
-rw-r--r--src/tests/data/test_messages/message.txt.signed-mimemodebin0 -> 260 bytes
-rw-r--r--src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line-2.asc11
-rw-r--r--src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line.asc11
-rw-r--r--src/tests/ffi-enc.cpp37
-rw-r--r--src/tests/ffi-key-sig.cpp5
-rw-r--r--src/tests/ffi-key.cpp27
-rw-r--r--src/tests/ffi.cpp32
-rw-r--r--src/tests/generatekey.cpp10
-rw-r--r--src/tests/key-add-userid.cpp2
-rw-r--r--src/tests/load-pgp.cpp13
-rw-r--r--src/tests/rnp_tests.cpp5
-rw-r--r--src/tests/rnp_tests.h3
-rw-r--r--src/tests/streams.cpp67
-rw-r--r--src/tests/support.cpp8
-rw-r--r--src/tests/support.h2
56 files changed, 1397 insertions, 472 deletions
diff --git a/src/common/uniwin.h b/src/common/uniwin.h
index 095c325..b7e2b52 100644
--- a/src/common/uniwin.h
+++ b/src/common/uniwin.h
@@ -35,9 +35,9 @@
#include <direct.h> /* for _getcwd() and _chdir() */
#ifdef _WIN64
-#define ssize_t __int64
+typedef __int64 ssize_t;
#else
-#define ssize_t long
+typedef long ssize_t;
#endif
#define STDIN_FILENO 0
diff --git a/src/fuzzing/keyring.c b/src/fuzzing/keyring.c
index bac4e13..ad16cd0 100644
--- a/src/fuzzing/keyring.c
+++ b/src/fuzzing/keyring.c
@@ -35,15 +35,14 @@ int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
#endif
{
- rnp_input_t input = NULL;
- rnp_result_t ret = 0;
- rnp_ffi_t ffi = NULL;
+ rnp_input_t input = NULL;
+ rnp_ffi_t ffi = NULL;
- ret = rnp_input_from_memory(&input, data, size, false);
+ (void) rnp_input_from_memory(&input, data, size, false);
- ret = rnp_ffi_create(&ffi, "GPG", "GPG");
- ret =
- rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS);
+ (void) rnp_ffi_create(&ffi, "GPG", "GPG");
+ (void) rnp_load_keys(
+ ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS);
rnp_input_destroy(input);
rnp_ffi_destroy(ffi);
diff --git a/src/fuzzing/keyring_kbx.c b/src/fuzzing/keyring_kbx.c
index 768e669..7c9ce60 100644
--- a/src/fuzzing/keyring_kbx.c
+++ b/src/fuzzing/keyring_kbx.c
@@ -35,14 +35,12 @@ int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
#endif
{
- rnp_input_t input = NULL;
- rnp_result_t ret = 0;
- rnp_ffi_t ffi = NULL;
+ rnp_input_t input = NULL;
+ rnp_ffi_t ffi = NULL;
- ret = rnp_input_from_memory(&input, data, size, false);
-
- ret = rnp_ffi_create(&ffi, "KBX", "G10");
- ret = rnp_load_keys(ffi, "KBX", input, RNP_LOAD_SAVE_PUBLIC_KEYS);
+ (void) rnp_input_from_memory(&input, data, size, false);
+ (void) rnp_ffi_create(&ffi, "KBX", "G10");
+ (void) rnp_load_keys(ffi, "KBX", input, RNP_LOAD_SAVE_PUBLIC_KEYS);
rnp_input_destroy(input);
rnp_ffi_destroy(ffi);
diff --git a/src/fuzzing/sigimport.c b/src/fuzzing/sigimport.c
index 35adeb7..89bd01a 100644
--- a/src/fuzzing/sigimport.c
+++ b/src/fuzzing/sigimport.c
@@ -35,14 +35,13 @@ int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
#endif
{
- rnp_input_t input = NULL;
- rnp_result_t ret = 0;
- rnp_ffi_t ffi = NULL;
+ rnp_input_t input = NULL;
+ rnp_ffi_t ffi = NULL;
- ret = rnp_input_from_memory(&input, data, size, false);
- ret = rnp_ffi_create(&ffi, "GPG", "GPG");
+ (void) rnp_input_from_memory(&input, data, size, false);
+ (void) rnp_ffi_create(&ffi, "GPG", "GPG");
char *results = NULL;
- ret = rnp_import_signatures(ffi, input, 0, &results);
+ (void) rnp_import_signatures(ffi, input, 0, &results);
rnp_buffer_destroy(results);
rnp_input_destroy(input);
rnp_ffi_destroy(ffi);
diff --git a/src/fuzzing/verify.c b/src/fuzzing/verify.c
index cd6c849..7dcfb01 100644
--- a/src/fuzzing/verify.c
+++ b/src/fuzzing/verify.c
@@ -39,16 +39,15 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
rnp_ffi_t ffi = NULL;
rnp_input_t input = NULL;
rnp_output_t output = NULL;
- rnp_result_t ret;
- ret = rnp_ffi_create(&ffi, "GPG", "GPG");
- ret = rnp_input_from_memory(&input, data, size, false);
- ret = rnp_output_to_null(&output);
+ (void) rnp_ffi_create(&ffi, "GPG", "GPG");
+ (void) rnp_input_from_memory(&input, data, size, false);
+ (void) rnp_output_to_null(&output);
rnp_op_verify_t op = NULL;
- ret = rnp_op_verify_create(&op, ffi, input, output);
- ret = rnp_op_verify_execute(op);
- ret = rnp_op_verify_destroy(op);
+ (void) rnp_op_verify_create(&op, ffi, input, output);
+ (void) rnp_op_verify_execute(op);
+ (void) rnp_op_verify_destroy(op);
rnp_input_destroy(input);
rnp_output_destroy(output);
diff --git a/src/fuzzing/verify_detached.c b/src/fuzzing/verify_detached.c
index 2afb59a..316e7a1 100644
--- a/src/fuzzing/verify_detached.c
+++ b/src/fuzzing/verify_detached.c
@@ -36,20 +36,19 @@ int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
#endif
{
- rnp_ffi_t ffi = NULL;
- rnp_input_t input = NULL;
- rnp_input_t msg_input = NULL;
- rnp_result_t ret;
+ rnp_ffi_t ffi = NULL;
+ rnp_input_t input = NULL;
+ rnp_input_t msg_input = NULL;
- ret = rnp_ffi_create(&ffi, "GPG", "GPG");
- ret = rnp_input_from_memory(&input, data, size, false);
+ (void) rnp_ffi_create(&ffi, "GPG", "GPG");
+ (void) rnp_input_from_memory(&input, data, size, false);
const char *msg = "message";
- ret = rnp_input_from_memory(&msg_input, (const uint8_t *) msg, strlen(msg), true);
+ (void) rnp_input_from_memory(&msg_input, (const uint8_t *) msg, strlen(msg), true);
rnp_op_verify_t verify = NULL;
- ret = rnp_op_verify_detached_create(&verify, ffi, msg_input, input);
- ret = rnp_op_verify_execute(verify);
- ret = rnp_op_verify_destroy(verify);
+ (void) rnp_op_verify_detached_create(&verify, ffi, msg_input, input);
+ (void) rnp_op_verify_execute(verify);
+ (void) rnp_op_verify_destroy(verify);
rnp_input_destroy(input);
rnp_input_destroy(msg_input);
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index 086ac57..3b4b444 100755
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -31,11 +31,15 @@ find_package(ZLIB REQUIRED)
# required packages
find_package(JSON-C 0.11 REQUIRED)
-if (CRYPTO_BACKEND_BOTAN)
- find_package(Botan2 2.14.0 REQUIRED)
+if (CRYPTO_BACKEND_BOTAN3)
+ find_package(Botan 3.0.0 REQUIRED)
+elseif (CRYPTO_BACKEND_BOTAN)
+ find_package(Botan 2.14.0 REQUIRED)
+ if(BOTAN_VERSION VERSION_GREATER_EQUAL 3.0.0)
+ set(CRYPTO_BACKEND_BOTAN3 1)
+ endif()
endif()
if (CRYPTO_BACKEND_OPENSSL)
- include(FindOpenSSL)
find_package(OpenSSL 1.1.1 REQUIRED)
include(FindOpenSSLFeatures)
if("${OPENSSL_VERSION}" VERSION_GREATER_EQUAL "3.0.0")
@@ -43,6 +47,10 @@ if (CRYPTO_BACKEND_OPENSSL)
endif()
endif()
+if(CRYPTO_BACKEND_BOTAN3)
+ set(CMAKE_CXX_STANDARD 20)
+endif()
+
# generate a config.h
include(CheckIncludeFileCXX)
include(CheckCXXSymbolExists)
@@ -75,7 +83,7 @@ function(backend_has_feature FEATURE RESULT_VARNAME)
if (CRYPTO_BACKEND_LOWERCASE STREQUAL "botan")
check_cxx_symbol_exists("BOTAN_HAS_${FEATURE}" botan/build.h ${RESULT_VARNAME})
else()
- message(STATUS "Looking for OpenSSL feature ${FEATURE}")
+ message(STATUS "Looking for OpenSSL feature ${FEATURE}")
OpenSSLHasFeature(${FEATURE} ${RESULT_VARNAME})
if (${RESULT_VARNAME})
message(STATUS "Looking for OpenSSL feature ${FEATURE} - found")
@@ -100,7 +108,7 @@ function(resolve_feature_state RNP_FEATURE BACKEND_FEATURES)
foreach(feature ${BACKEND_FEATURES})
backend_has_feature("${feature}" _has_${feature})
- if (NOT ${_has_${feature}})
+ if (NOT _has_${feature})
set(${RNP_FEATURE} Off CACHE STRING "Autodetected" FORCE)
message(${MESSAGE_TYPE} "${RNP_FEATURE} requires ${CRYPTO_BACKEND} feature which is missing: ${feature}. ${OUTCOME}.")
return()
@@ -129,7 +137,7 @@ endfunction()
if(CRYPTO_BACKEND_BOTAN)
# check botan's enabled features
- set(CMAKE_REQUIRED_INCLUDES "${BOTAN2_INCLUDE_DIRS}")
+ set(CMAKE_REQUIRED_INCLUDES "${BOTAN_INCLUDE_DIRS}")
set(_botan_required_features
# base
BIGINT FFI HEX_CODEC PGP_S2K
@@ -142,12 +150,17 @@ if(CRYPTO_BACKEND_BOTAN)
# hash
CRC24 HASH MD5 SHA1 SHA2_32 SHA2_64 SHA3
# public-key core
- DL_GROUP DL_PUBLIC_KEY_FAMILY ECC_GROUP ECC_PUBLIC_KEY_CRYPTO PUBLIC_KEY_CRYPTO
+ DL_GROUP ECC_GROUP ECC_PUBLIC_KEY_CRYPTO PUBLIC_KEY_CRYPTO # Botan-2: DL_PUBLIC_KEY_FAMILY Botan-3: DL_SCHEME, see switch below
# public-key algs
CURVE_25519 DSA ECDH ECDSA ED25519 ELGAMAL RSA
# public-key operations etc
EME_PKCS1v15 EMSA_PKCS1 EMSA_RAW KDF_BASE RFC3394_KEYWRAP SP800_56A
)
+ if(BOTAN_VERSION VERSION_LESS 3.0.0)
+ set(_botan_required_features ${_botan_required_features} DL_PUBLIC_KEY_FAMILY)
+ else()
+ set(_botan_required_features ${_botan_required_features} DL_SCHEME RAW_HASH_FN)
+ endif()
foreach(feature ${_botan_required_features})
check_cxx_symbol_exists("BOTAN_HAS_${feature}" botan/build.h _botan_has_${feature})
if (NOT _botan_has_${feature})
@@ -181,19 +194,26 @@ if(CRYPTO_BACKEND_OPENSSL)
RSAENCRYPTION DSAENCRYPTION DHKEYAGREEMENT ID-ECPUBLICKEY X25519 ED25519
)
foreach(feature ${_openssl_required_features})
- message(STATUS "Looking for OpenSSL feature ${feature}")
- OpenSSLHasFeature("${feature}" _openssl_has_${feature})
+ backend_has_feature("${feature}" _openssl_has_${feature})
if (NOT _openssl_has_${feature})
message(FATAL_ERROR "A required OpenSSL feature is missing: ${feature}")
endif()
- message(STATUS "Looking for OpenSSL feature ${feature} - found")
endforeach()
+ if (CRYPTO_BACKEND_OPENSSL3)
+ backend_has_feature("LEGACY" CRYPTO_BACKEND_OPENSSL3_LEGACY)
+ endif()
+
resolve_feature_state(ENABLE_BRAINPOOL "BRAINPOOLP256R1;BRAINPOOLP384R1;BRAINPOOLP512R1")
- resolve_feature_state(ENABLE_IDEA "IDEA-ECB;IDEA-CBC")
- resolve_feature_state(ENABLE_BLOWFISH "BF-ECB")
- resolve_feature_state(ENABLE_CAST5 "CAST5-ECB")
- resolve_feature_state(ENABLE_RIPEMD160 "RIPEMD160")
+ # Not all of the OpenSSL installations have legacy crypto provider
+ resolve_feature_state(ENABLE_IDEA "IDEA-ECB;IDEA-CBC;LEGACY")
+ resolve_feature_state(ENABLE_BLOWFISH "BF-ECB;LEGACY")
+ resolve_feature_state(ENABLE_CAST5 "CAST5-ECB;LEGACY")
+ if("${OPENSSL_VERSION}" VERSION_GREATER_EQUAL "3.0.7")
+ resolve_feature_state(ENABLE_RIPEMD160 "RIPEMD160")
+ else()
+ resolve_feature_state(ENABLE_RIPEMD160 "RIPEMD160;LEGACY")
+ endif()
resolve_feature_state(ENABLE_AEAD "AES-128-OCB;AES-192-OCB;AES-256-OCB")
openssl_nope(ENABLE_SM2 "it's on our roadmap, see https://github.com/rnpgp/rnp/issues/1877")
#resolve_feature_state(ENABLE_SM2 "SM2;SM3;SM4-ECB")
@@ -320,15 +340,16 @@ target_include_directories(librnp-obj
PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}"
"${PROJECT_SOURCE_DIR}/src"
+ "${SEXPP_INCLUDE_DIRS}"
)
target_link_libraries(librnp-obj PRIVATE JSON-C::JSON-C)
if (CRYPTO_BACKEND_BOTAN)
- target_link_libraries(librnp-obj PRIVATE Botan2::Botan2)
+ target_link_libraries(librnp-obj PRIVATE Botan::Botan)
elseif (CRYPTO_BACKEND_OPENSSL)
target_link_libraries(librnp-obj PRIVATE OpenSSL::Crypto)
endif()
-target_link_libraries(librnp-obj PRIVATE sexp)
+target_link_libraries(librnp-obj PRIVATE sexpp)
set_target_properties(librnp-obj PROPERTIES CXX_VISIBILITY_PRESET hidden)
if (TARGET BZip2::BZip2)
@@ -384,7 +405,7 @@ foreach (prop LINK_LIBRARIES INTERFACE_LINK_LIBRARIES INCLUDE_DIRECTORIES INTERF
get_target_property(val librnp-obj ${prop})
if (BUILD_SHARED_LIBS)
set_property(TARGET librnp-static PROPERTY ${prop} ${val})
- list(REMOVE_ITEM val "$<LINK_ONLY:sexp>")
+ list(REMOVE_ITEM val "$<LINK_ONLY:sexpp>")
set_property(TARGET librnp PROPERTY ${prop} ${val})
else()
set_property(TARGET librnp PROPERTY ${prop} ${val})
@@ -414,15 +435,14 @@ else()
endif()
# add these to the rnp-targets export
-# On Unix like systems we will build/install/pack shared and static libraries librnp.so and librnp.a
-# On Windows we will build/install/pack dynamic, import and static libraries rnp.dll, rnp.lib and rnp-static.lib
+# On Unix like systems we will build/install/pack either shared library librnp.so or static librnp.a
+# On Windows we will build/install/pack either dynamic and import libraries rnp.dll, rnp.lib or static library rnp-static.lib
-# If a client application uses shared rnp library, sexp is statically linked to librnp.so
-# If a client application uses static rnp library, it still needs libsexp.a
+# If a client application uses shared rnp library, sexpp is statically linked to librnp.so and libsexpp.a is not installed
+# If a client application uses static rnp library, it still needs libsexpp.a and it is installed
if (BUILD_SHARED_LIBS)
-# both static and shared libraries
-install(TARGETS librnp
+ install(TARGETS librnp
EXPORT rnp-targets
LIBRARY
DESTINATION "${CMAKE_INSTALL_LIBDIR}"
@@ -433,31 +453,34 @@ install(TARGETS librnp
COMPONENT development
)
- install(TARGETS librnp-static sexp
- EXPORT rnp-targets
- ARCHIVE
- DESTINATION "${CMAKE_INSTALL_LIBDIR}"
- COMPONENT development
- )
+# install dll only for windows
+ if (WIN32)
+ install(TARGETS librnp
+ RUNTIME
+ DESTINATION "${CMAKE_INSTALL_BINDIR}"
+ COMPONENT runtime
+ )
+ endif(WIN32)
else(BUILD_SHARED_LIBS)
-# static libraries only
-install(TARGETS librnp sexp
+# static libraries
+# install libsexpp unless system-installed libsexpp is used
+ if (SYSTEM_LIBSEXPP)
+ install(TARGETS librnp
+ EXPORT rnp-targets
+ ARCHIVE
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ COMPONENT development
+ )
+ else (SYSTEM_LIBSEXPP)
+ install(TARGETS librnp sexpp
EXPORT rnp-targets
ARCHIVE
DESTINATION "${CMAKE_INSTALL_LIBDIR}"
COMPONENT development
-)
+ )
+ endif (SYSTEM_LIBSEXPP)
endif(BUILD_SHARED_LIBS)
-# install dll only for windows
-if (WIN32)
- install(TARGETS librnp
- RUNTIME
- DESTINATION "${CMAKE_INSTALL_BINDIR}"
- COMPONENT runtime
- )
-endif(WIN32)
-
# install headers
install(
FILES
diff --git a/src/lib/config.h.in b/src/lib/config.h.in
index f8880d5..afb2cfe 100644
--- a/src/lib/config.h.in
+++ b/src/lib/config.h.in
@@ -51,8 +51,10 @@
#cmakedefine HAVE__TEMPNAM
#cmakedefine CRYPTO_BACKEND_BOTAN
+#cmakedefine CRYPTO_BACKEND_BOTAN3
#cmakedefine CRYPTO_BACKEND_OPENSSL
#cmakedefine CRYPTO_BACKEND_OPENSSL3
+#cmakedefine CRYPTO_BACKEND_OPENSSL3_LEGACY
#cmakedefine ENABLE_SM2
#cmakedefine ENABLE_AEAD
@@ -67,6 +69,6 @@
* we assume to be bundled with a sane implementation of std::regex. */
#if !defined(__GNUC__) || defined(_GLIBCXX_USE_CXX11_ABI) || \
(defined(WIN32) && !defined(MSYS)) || \
- ((defined(__clang__) && (__clang_major__ >= 4)) )
+ ((defined(__clang__) && (__clang_major__ >= 4)))
#define RNP_USE_STD_REGEX 1
#endif
diff --git a/src/lib/crypto.cpp b/src/lib/crypto.cpp
index 2634604..da3cba1 100644
--- a/src/lib/crypto.cpp
+++ b/src/lib/crypto.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2020, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023, [Ribose Inc](https://www.ribose.com).
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
@@ -128,7 +128,9 @@ pgp_generate_seckey(const rnp_keygen_crypto_params_t &crypto,
seckey.material.ec.curve = crypto.ecc.curve;
break;
}
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
case PGP_PKA_ECDSA:
case PGP_PKA_SM2:
if (!curve_supported(crypto.ecc.curve)) {
diff --git a/src/lib/crypto/backend_version.cpp b/src/lib/crypto/backend_version.cpp
index 859b048..32af839 100644
--- a/src/lib/crypto/backend_version.cpp
+++ b/src/lib/crypto/backend_version.cpp
@@ -72,7 +72,7 @@ backend_version()
if (version[0]) {
return version;
}
- const char *reg = "OpenSSL (([0-9]\\.[0-9]\\.[0-9])[a-z]*(-beta[0-9])*(-dev)*) ";
+ const char *reg = "OpenSSL (([0-9]+\\.[0-9]+\\.[0-9]+)[a-z]*(-[a-z0-9]+)*) ";
#ifndef RNP_USE_STD_REGEX
static regex_t r;
regmatch_t matches[5];
@@ -84,7 +84,9 @@ backend_version()
return "unknown";
}
}
- if (regexec(&r, ver, 5, matches, 0) != 0) {
+ int res = regexec(&r, ver, 5, matches, 0);
+ if (res != 0) {
+ RNP_LOG("regexec() failed on %s: %d", ver, res);
return "unknown";
}
assert(sizeof(version) > matches[1].rm_eo - matches[1].rm_so);
@@ -95,6 +97,7 @@ backend_version()
std::smatch result;
std::string ver = OpenSSL_version(OPENSSL_VERSION);
if (!std::regex_search(ver, result, re)) {
+ RNP_LOG("std::regex_search failed on \"%s\"", ver.c_str());
return "unknown";
}
assert(sizeof(version) > result[1].str().size());
@@ -109,7 +112,10 @@ backend_version()
#if defined(CRYPTO_BACKEND_OPENSSL3)
#if defined(ENABLE_IDEA) || defined(ENABLE_CAST5) || defined(ENABLE_BLOWFISH) || \
- defined(ENABLE_RIPEMD160)
+ (defined(ENABLE_RIPEMD160) && OPENSSL_VERSION_NUMBER < 0x30000070L)
+#if !defined(CRYPTO_BACKEND_OPENSSL3_LEGACY)
+#error "OpenSSL doesn't have legacy provider, however one of the features enables it's load."
+#endif
#define OPENSSL_LOAD_LEGACY
#endif
diff --git a/src/lib/crypto/bn.h b/src/lib/crypto/bn.h
index 26cc547..a4cfa1a 100644
--- a/src/lib/crypto/bn.h
+++ b/src/lib/crypto/bn.h
@@ -61,4 +61,82 @@ bool bn2mpi(const bignum_t *bn, pgp_mpi_t *val);
size_t bn_num_bytes(const bignum_t &a);
+#if defined(CRYPTO_BACKEND_OPENSSL)
+namespace rnp {
+class bn {
+ BIGNUM *_bn;
+
+ public:
+ bn(BIGNUM *val = NULL) : _bn(val)
+ {
+ }
+
+ bn(const pgp_mpi_t &val) : _bn(mpi2bn(&val))
+ {
+ }
+
+ ~bn()
+ {
+ BN_free(_bn);
+ }
+
+ void
+ set(BIGNUM *val = NULL) noexcept
+ {
+ BN_free(_bn);
+ _bn = val;
+ }
+
+ void
+ set(const pgp_mpi_t &val) noexcept
+ {
+ BN_free(_bn);
+ _bn = mpi2bn(&val);
+ }
+
+ BIGNUM **
+ ptr() noexcept
+ {
+ set();
+ return &_bn;
+ }
+
+ BIGNUM *
+ get() noexcept
+ {
+ return _bn;
+ }
+
+ BIGNUM *
+ own() noexcept
+ {
+ auto res = _bn;
+ _bn = NULL;
+ return res;
+ }
+
+ size_t
+ bytes() const noexcept
+ {
+ return BN_num_bytes(_bn);
+ }
+
+ bool
+ bin(uint8_t *b) const noexcept
+ {
+ if (!b) {
+ return false;
+ }
+ return BN_bn2bin(_bn, b) >= 0;
+ }
+
+ bool
+ mpi(pgp_mpi_t &mpi) const noexcept
+ {
+ return bn2mpi(_bn, &mpi);
+ }
+};
+}; // namespace rnp
+#endif
+
#endif
diff --git a/src/lib/crypto/cipher.hpp b/src/lib/crypto/cipher.hpp
index c9edf15..f683b76 100644
--- a/src/lib/crypto/cipher.hpp
+++ b/src/lib/crypto/cipher.hpp
@@ -58,7 +58,10 @@ class Cipher {
const uint8_t *input,
size_t input_length,
size_t * input_consumed) = 0;
- // process final block and perform any padding
+ /**
+ * @brief Finalize cipher. For AEAD mode, depending on backend, may require whole
+ * authentication tag to be present in input.
+ */
virtual bool finish(uint8_t * output,
size_t output_length,
size_t * output_written,
diff --git a/src/lib/crypto/cipher_botan.cpp b/src/lib/crypto/cipher_botan.cpp
index c2c4ab3..6f4a4fc 100644
--- a/src/lib/crypto/cipher_botan.cpp
+++ b/src/lib/crypto/cipher_botan.cpp
@@ -64,8 +64,12 @@ Cipher_Botan::create(pgp_symm_alg_t alg, const std::string &name, bool encrypt)
return nullptr;
}
#endif
- auto cipher = Botan::Cipher_Mode::create(
- name, encrypt ? Botan::Cipher_Dir::ENCRYPTION : Botan::Cipher_Dir::DECRYPTION);
+#if defined(CRYPTO_BACKEND_BOTAN3)
+ auto dir = encrypt ? Botan::Cipher_Dir::Encryption : Botan::Cipher_Dir::Decryption;
+#else
+ auto dir = encrypt ? Botan::Cipher_Dir::ENCRYPTION : Botan::Cipher_Dir::DECRYPTION;
+#endif
+ auto cipher = Botan::Cipher_Mode::create(name, dir);
if (!cipher) {
RNP_LOG("Failed to create cipher '%s'", name.c_str());
return nullptr;
diff --git a/src/lib/crypto/dl_ossl.cpp b/src/lib/crypto/dl_ossl.cpp
index 1e96218..4845baa 100644
--- a/src/lib/crypto/dl_ossl.cpp
+++ b/src/lib/crypto/dl_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021, 2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -33,6 +33,34 @@
#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/evp.h>
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#endif
+
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+static OSSL_PARAM *
+dl_build_params(bignum_t *p, bignum_t *q, bignum_t *g, bignum_t *y, bignum_t *x)
+{
+ OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
+ if (!bld) {
+ return NULL; // LCOV_EXCL_LINE
+ }
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) ||
+ (q && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q)) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, y) ||
+ (x && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, x))) {
+ /* LCOV_EXCL_START */
+ OSSL_PARAM_BLD_free(bld);
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ OSSL_PARAM *param = OSSL_PARAM_BLD_to_param(bld);
+ OSSL_PARAM_BLD_free(bld);
+ return param;
+}
+#endif
EVP_PKEY *
dl_load_key(const pgp_mpi_t &mp,
@@ -41,63 +69,89 @@ dl_load_key(const pgp_mpi_t &mp,
const pgp_mpi_t &my,
const pgp_mpi_t *mx)
{
- DH * dh = NULL;
EVP_PKEY *evpkey = NULL;
- bignum_t *p = mpi2bn(&mp);
- bignum_t *q = mq ? mpi2bn(mq) : NULL;
- bignum_t *g = mpi2bn(&mg);
- bignum_t *y = mpi2bn(&my);
- bignum_t *x = mx ? mpi2bn(mx) : NULL;
+ rnp::bn p(mpi2bn(&mp));
+ rnp::bn q(mq ? mpi2bn(mq) : NULL);
+ rnp::bn g(mpi2bn(&mg));
+ rnp::bn y(mpi2bn(&my));
+ rnp::bn x(mx ? mpi2bn(mx) : NULL);
- if (!p || (mq && !q) || !g || !y || (mx && !x)) {
+ if (!p.get() || (mq && !q.get()) || !g.get() || !y.get() || (mx && !x.get())) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- dh = DH_new();
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ OSSL_PARAM *params = dl_build_params(p.get(), q.get(), g.get(), y.get(), x.get());
+ if (!params) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to build dsa params");
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DH, NULL);
+ if (!ctx) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to create dl context");
+ OSSL_PARAM_free(params);
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ if ((EVP_PKEY_fromdata_init(ctx) != 1) ||
+ (EVP_PKEY_fromdata(
+ ctx, &evpkey, mx ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY, params) != 1)) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to create key from data");
+ evpkey = NULL;
+ /* LCOV_EXCL_END */
+ }
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+ return evpkey;
+#else
+ DH *dh = DH_new();
if (!dh) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- int res;
/* line below must not fail */
- res = DH_set0_pqg(dh, p, q, g);
+ int res = DH_set0_pqg(dh, p.own(), q.own(), g.own());
assert(res == 1);
if (res < 1) {
goto done;
}
- p = NULL;
- q = NULL;
- g = NULL;
/* line below must not fail */
- res = DH_set0_key(dh, y, x);
+ res = DH_set0_key(dh, y.own(), x.own());
assert(res == 1);
if (res < 1) {
goto done;
}
- y = NULL;
- x = NULL;
evpkey = EVP_PKEY_new();
if (!evpkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("allocation failed");
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_set1_DH(evpkey, dh) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set key: %lu", ERR_peek_last_error());
EVP_PKEY_free(evpkey);
evpkey = NULL;
+ /* LCOV_EXCL_END */
}
done:
DH_free(dh);
- bn_free(p);
- bn_free(q);
- bn_free(g);
- bn_free(y);
- bn_free(x);
return evpkey;
+#endif
}
+#if !defined(CRYPTO_BACKEND_OPENSSL3)
static rnp_result_t
dl_validate_secret_key(EVP_PKEY *dlkey, const pgp_mpi_t &mx)
{
@@ -117,22 +171,28 @@ dl_validate_secret_key(EVP_PKEY *dlkey, const pgp_mpi_t &mx)
bignum_t *cy = bn_new();
if (!x || !cy || !ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed");
goto done;
+ /* LCOV_EXCL_END */
}
if (!q) {
/* if q is NULL then group order is (p - 1) / 2 */
p1 = BN_dup(p);
if (!p1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed");
goto done;
+ /* LCOV_EXCL_END */
}
int res;
res = BN_rshift(p1, p1, 1);
assert(res == 1);
if (res < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("BN_rshift failed.");
goto done;
+ /* LCOV_EXCL_END */
}
q = p1;
}
@@ -154,6 +214,7 @@ done:
bn_free(p1);
return ret;
}
+#endif
rnp_result_t
dl_validate_key(EVP_PKEY *pkey, const pgp_mpi_t *x)
@@ -161,8 +222,10 @@ dl_validate_key(EVP_PKEY *pkey, const pgp_mpi_t *x)
rnp_result_t ret = RNP_ERROR_GENERIC;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
int res;
res = EVP_PKEY_param_check(ctx);
@@ -181,6 +244,12 @@ dl_validate_key(EVP_PKEY *pkey, const pgp_mpi_t *x)
goto done;
}
}
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ res = x ? EVP_PKEY_pairwise_check(ctx) : EVP_PKEY_public_check(ctx);
+ if (res == 1) {
+ ret = RNP_SUCCESS;
+ }
+#else
res = EVP_PKEY_public_check(ctx);
if (res < 0) {
RNP_LOG("Key validation error: %lu", ERR_peek_last_error());
@@ -194,6 +263,7 @@ dl_validate_key(EVP_PKEY *pkey, const pgp_mpi_t *x)
goto done;
}
ret = dl_validate_secret_key(pkey, *x);
+#endif
done:
EVP_PKEY_CTX_free(ctx);
return ret;
diff --git a/src/lib/crypto/dsa_ossl.cpp b/src/lib/crypto/dsa_ossl.cpp
index 1fb75b5..3f91b72 100644
--- a/src/lib/crypto/dsa_ossl.cpp
+++ b/src/lib/crypto/dsa_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021, 2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -35,6 +35,10 @@
#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/evp.h>
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#endif
#define DSA_MAX_Q_BITLEN 256
@@ -83,66 +87,118 @@ done:
return res;
}
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+static OSSL_PARAM *
+dsa_build_params(bignum_t *p, bignum_t *q, bignum_t *g, bignum_t *y, bignum_t *x)
+{
+ OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
+ if (!bld) {
+ return NULL; // LCOV_EXCL_LINE
+ }
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, y) ||
+ (x && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, x))) {
+ /* LCOV_EXCL_START */
+ OSSL_PARAM_BLD_free(bld);
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ OSSL_PARAM *param = OSSL_PARAM_BLD_to_param(bld);
+ OSSL_PARAM_BLD_free(bld);
+ return param;
+}
+#endif
+
static EVP_PKEY *
dsa_load_key(const pgp_dsa_key_t *key, bool secret = false)
{
- DSA * dsa = NULL;
EVP_PKEY *evpkey = NULL;
- bignum_t *p = mpi2bn(&key->p);
- bignum_t *q = mpi2bn(&key->q);
- bignum_t *g = mpi2bn(&key->g);
- bignum_t *y = mpi2bn(&key->y);
- bignum_t *x = secret ? mpi2bn(&key->x) : NULL;
+ rnp::bn p(mpi2bn(&key->p));
+ rnp::bn q(mpi2bn(&key->q));
+ rnp::bn g(mpi2bn(&key->g));
+ rnp::bn y(mpi2bn(&key->y));
+ rnp::bn x(secret ? mpi2bn(&key->x) : NULL);
- if (!p || !q || !g || !y || (secret && !x)) {
+ if (!p.get() || !q.get() || !g.get() || !y.get() || (secret && !x.get())) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- dsa = DSA_new();
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ OSSL_PARAM *params = dsa_build_params(p.get(), q.get(), g.get(), y.get(), x.get());
+ if (!params) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to build dsa params");
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL);
+ if (!ctx) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to create dsa context");
+ OSSL_PARAM_free(params);
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ if ((EVP_PKEY_fromdata_init(ctx) != 1) ||
+ (EVP_PKEY_fromdata(
+ ctx, &evpkey, secret ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY, params) != 1)) {
+ RNP_LOG("failed to create key from data");
+ evpkey = NULL;
+ }
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+ return evpkey;
+#else
+ DSA *dsa = DSA_new();
if (!dsa) {
+ /* LCOV_EXCL_START */
RNP_LOG("Out of memory");
goto done;
+ /* LCOV_EXCL_END */
}
- if (DSA_set0_pqg(dsa, p, q, g) != 1) {
+ if (DSA_set0_pqg(dsa, p.own(), q.own(), g.own()) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set pqg. Error: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
- p = NULL;
- q = NULL;
- g = NULL;
- if (DSA_set0_key(dsa, y, x) != 1) {
+ if (DSA_set0_key(dsa, y.own(), x.own()) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Secret key load error: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
- y = NULL;
- x = NULL;
evpkey = EVP_PKEY_new();
if (!evpkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("allocation failed");
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_set1_DSA(evpkey, dsa) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set key: %lu", ERR_peek_last_error());
EVP_PKEY_free(evpkey);
evpkey = NULL;
+ /* LCOV_EXCL_END */
}
done:
DSA_free(dsa);
- bn_free(p);
- bn_free(q);
- bn_free(g);
- bn_free(y);
- bn_free(x);
return evpkey;
+#endif
}
rnp_result_t
dsa_validate_key(rnp::RNG *rng, const pgp_dsa_key_t *key, bool secret)
{
/* OpenSSL doesn't implement key checks for the DSA, however we may use DL via DH */
- EVP_PKEY *pkey = dl_load_key(key->p, &key->q, key->g, key->y, NULL);
+ EVP_PKEY *pkey = dl_load_key(key->p, &key->q, key->g, key->y, secret ? &key->x : NULL);
if (!pkey) {
RNP_LOG("Failed to load key");
return RNP_ERROR_BAD_PARAMETERS;
@@ -175,8 +231,10 @@ dsa_sign(rnp::RNG * rng,
/* init context and sign */
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_sign_init(ctx) <= 0) {
RNP_LOG("Failed to initialize signing: %lu", ERR_peek_last_error());
@@ -216,8 +274,10 @@ dsa_verify(const pgp_dsa_signature_t *sig,
/* init context and sign */
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_verify_init(ctx) <= 0) {
RNP_LOG("Failed to initialize verify: %lu", ERR_peek_last_error());
@@ -238,6 +298,43 @@ done:
return ret;
}
+static bool
+dsa_extract_key(EVP_PKEY *pkey, pgp_dsa_key_t &key)
+{
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ rnp::bn p;
+ rnp::bn q;
+ rnp::bn g;
+ rnp::bn y;
+ rnp::bn x;
+
+ bool res = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_P, p.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_Q, q.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_G, g.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, y.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, x.ptr());
+ return res && p.mpi(key.p) && q.mpi(key.q) && g.mpi(key.g) && y.mpi(key.y) && x.mpi(key.x);
+#else
+ const DSA *dsa = EVP_PKEY_get0_DSA(pkey);
+ if (!dsa) {
+ RNP_LOG("Failed to retrieve DSA key: %lu", ERR_peek_last_error());
+ return false;
+ }
+
+ const bignum_t *p = DSA_get0_p(dsa);
+ const bignum_t *q = DSA_get0_q(dsa);
+ const bignum_t *g = DSA_get0_g(dsa);
+ const bignum_t *y = DSA_get0_pub_key(dsa);
+ const bignum_t *x = DSA_get0_priv_key(dsa);
+
+ if (!p || !q || !g || !y || !x) {
+ return false;
+ }
+ return bn2mpi(p, &key.p) && bn2mpi(q, &key.q) && bn2mpi(g, &key.g) && bn2mpi(y, &key.y) &&
+ bn2mpi(x, &key.x);
+#endif
+}
+
rnp_result_t
dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits)
{
@@ -246,7 +343,6 @@ dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits)
}
rnp_result_t ret = RNP_ERROR_GENERIC;
- const DSA * dsa = NULL;
EVP_PKEY * pkey = NULL;
EVP_PKEY * parmkey = NULL;
EVP_PKEY_CTX *ctx = NULL;
@@ -254,12 +350,16 @@ dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits)
/* Generate DSA params */
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
return ret;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_paramgen_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx, keylen) <= 0) {
RNP_LOG("Failed to set key bits: %lu", ERR_peek_last_error());
@@ -293,32 +393,10 @@ dsa_generate(rnp::RNG *rng, pgp_dsa_key_t *key, size_t keylen, size_t qbits)
RNP_LOG("DSA keygen failed: %lu", ERR_peek_last_error());
goto done;
}
- dsa = EVP_PKEY_get0_DSA(pkey);
- if (!dsa) {
- RNP_LOG("Failed to retrieve DSA key: %lu", ERR_peek_last_error());
- goto done;
- }
- const bignum_t *p;
- const bignum_t *q;
- const bignum_t *g;
- const bignum_t *y;
- const bignum_t *x;
- p = DSA_get0_p(dsa);
- q = DSA_get0_q(dsa);
- g = DSA_get0_g(dsa);
- y = DSA_get0_pub_key(dsa);
- x = DSA_get0_priv_key(dsa);
- if (!p || !q || !g || !y || !x) {
- ret = RNP_ERROR_BAD_STATE;
- goto done;
+ if (dsa_extract_key(pkey, *key)) {
+ ret = RNP_SUCCESS;
}
- bn2mpi(p, &key->p);
- bn2mpi(q, &key->q);
- bn2mpi(g, &key->g);
- bn2mpi(y, &key->y);
- bn2mpi(x, &key->x);
- ret = RNP_SUCCESS;
done:
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(parmkey);
diff --git a/src/lib/crypto/ec_ossl.cpp b/src/lib/crypto/ec_ossl.cpp
index 6974b4c..46c4677 100644
--- a/src/lib/crypto/ec_ossl.cpp
+++ b/src/lib/crypto/ec_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021, 2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -36,6 +36,10 @@
#include <openssl/objects.h>
#include <openssl/err.h>
#include <openssl/ec.h>
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#endif
static bool
ec_is_raw_key(const pgp_curve_t curve)
@@ -61,26 +65,34 @@ ec_generate_pkey(const pgp_pubkey_alg_t alg_id, const pgp_curve_t curve)
}
int nid = OBJ_sn2nid(ec_desc->openssl_name);
if (nid == NID_undef) {
+ /* LCOV_EXCL_START */
RNP_LOG("Unknown SN: %s", ec_desc->openssl_name);
return NULL;
+ /* LCOV_EXCL_END */
}
bool raw = ec_is_raw_key(curve);
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(raw ? nid : EVP_PKEY_EC, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
return NULL;
+ /* LCOV_EXCL_END */
}
EVP_PKEY *pkey = NULL;
if (EVP_PKEY_keygen_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (!raw && (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) <= 0)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set curve nid: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
- RNP_LOG("EC keygen failed: %lu", ERR_peek_last_error());
+ RNP_LOG("EC keygen failed: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
}
done:
EVP_PKEY_CTX_free(ctx);
@@ -94,8 +106,10 @@ ec_write_raw_seckey(EVP_PKEY *pkey, pgp_ec_key_t *key)
static_assert(sizeof(key->x.mpi) > 32, "mpi is too small.");
key->x.len = sizeof(key->x.mpi);
if (EVP_PKEY_get_raw_private_key(pkey, key->x.mpi, &key->x.len) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed get raw private key: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
assert(key->x.len == 32);
if (EVP_PKEY_id(pkey) == EVP_PKEY_X25519) {
@@ -107,6 +121,30 @@ ec_write_raw_seckey(EVP_PKEY *pkey, pgp_ec_key_t *key)
return true;
}
+static bool
+ec_write_seckey(EVP_PKEY *pkey, pgp_mpi_t &key)
+{
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ rnp::bn x;
+ return EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, x.ptr()) &&
+ bn2mpi(x.get(), &key);
+#else
+ const bignum_t *x = NULL;
+ const EC_KEY * ec = EVP_PKEY_get0_EC_KEY(pkey);
+ if (!ec) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("Failed to retrieve EC key: %lu", ERR_peek_last_error());
+ return false;
+ /* LCOV_EXCL_END */
+ }
+ x = EC_KEY_get0_private_key(ec);
+ if (!x) {
+ return false;
+ }
+ return bn2mpi(x, &key);
+#endif
+}
+
rnp_result_t
ec_generate(rnp::RNG * rng,
pgp_ec_key_t * key,
@@ -125,24 +163,19 @@ ec_generate(rnp::RNG * rng,
EVP_PKEY_free(pkey);
return ret;
}
- const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
- if (!ec) {
- RNP_LOG("Failed to retrieve EC key: %lu", ERR_peek_last_error());
- goto done;
- }
if (!ec_write_pubkey(pkey, key->p, curve)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to write pubkey.");
goto done;
+ /* LCOV_EXCL_END */
}
- const bignum_t *x;
- x = EC_KEY_get0_private_key(ec);
- if (!x) {
- ret = RNP_ERROR_BAD_STATE;
+ if (!ec_write_seckey(pkey, key->x)) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("Failed to write seckey.");
goto done;
+ /* LCOV_EXCL_END */
}
- if (bn2mpi(x, &key->x)) {
- ret = RNP_SUCCESS;
- }
+ ret = RNP_SUCCESS;
done:
EVP_PKEY_free(pkey);
return ret;
@@ -161,7 +194,7 @@ ec_load_raw_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, int nid)
EVP_PKEY *evpkey =
EVP_PKEY_new_raw_public_key(nid, NULL, &keyp.mpi[1], mpi_bytes(&keyp) - 1);
if (!evpkey) {
- RNP_LOG("Failed to load public key: %lu", ERR_peek_last_error());
+ RNP_LOG("Failed to load public key: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
}
return evpkey;
}
@@ -189,10 +222,68 @@ ec_load_raw_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, int nid)
evpkey = EVP_PKEY_new_raw_private_key(nid, NULL, prkey.data(), 32);
}
if (!evpkey) {
- RNP_LOG("Failed to load private key: %lu", ERR_peek_last_error());
+ RNP_LOG("Failed to load private key: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
+ }
+ return evpkey;
+}
+
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+static OSSL_PARAM *
+ec_build_params(const pgp_mpi_t &p, bignum_t *x, const char *curve)
+{
+ OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
+ if (!bld) {
+ return NULL;
+ }
+ if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, curve, 0) ||
+ !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, p.mpi, p.len) ||
+ (x && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, x))) {
+ /* LCOV_EXCL_START */
+ OSSL_PARAM_BLD_free(bld);
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ OSSL_PARAM *param = OSSL_PARAM_BLD_to_param(bld);
+ OSSL_PARAM_BLD_free(bld);
+ return param;
+}
+
+static EVP_PKEY *
+ec_load_key_openssl3(const pgp_mpi_t & keyp,
+ const pgp_mpi_t * keyx,
+ const ec_curve_desc_t *curv_desc)
+{
+ rnp::bn x(keyx ? mpi2bn(keyx) : NULL);
+ OSSL_PARAM *params = ec_build_params(keyp, x.get(), curv_desc->openssl_name);
+ if (!params) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to build ec params");
+ return NULL;
+ /* LCOV_EXCL_END */
+ }
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
+ if (!ctx) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to create ec context");
+ OSSL_PARAM_free(params);
+ return NULL;
+ /* LCOV_EXCL_END */
}
+ EVP_PKEY *evpkey = NULL;
+ if ((EVP_PKEY_fromdata_init(ctx) != 1) ||
+ (EVP_PKEY_fromdata(
+ ctx, &evpkey, keyx ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY, params) != 1)) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("failed to create ec key from data");
+ /* Some version of OpenSSL may leave evpkey non-NULL after failure, so let's be safe */
+ evpkey = NULL;
+ /* LCOV_EXCL_END */
+ }
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
return evpkey;
}
+#endif
EVP_PKEY *
ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve)
@@ -208,20 +299,27 @@ ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve)
}
int nid = OBJ_sn2nid(curv_desc->openssl_name);
if (nid == NID_undef) {
+ /* LCOV_EXCL_START */
RNP_LOG("Unknown SN: %s", curv_desc->openssl_name);
return NULL;
+ /* LCOV_EXCL_END */
}
/* EdDSA and X25519 keys are loaded in a different way */
if (ec_is_raw_key(curve)) {
return ec_load_raw_key(keyp, keyx, nid);
}
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ return ec_load_key_openssl3(keyp, keyx, curv_desc);
+#else
EC_KEY *ec = EC_KEY_new_by_curve_name(nid);
if (!ec) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create EC key with group %d (%s): %s",
nid,
curv_desc->openssl_name,
ERR_reason_error_string(ERR_peek_last_error()));
return NULL;
+ /* LCOV_EXCL_END */
}
bool res = false;
@@ -229,22 +327,30 @@ ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve)
EVP_PKEY *pkey = NULL;
EC_POINT *p = EC_POINT_new(EC_KEY_get0_group(ec));
if (!p) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to allocate point: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EC_POINT_oct2point(EC_KEY_get0_group(ec), p, keyp.mpi, keyp.len, NULL) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to decode point: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EC_KEY_set_public_key(ec, p) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set public key: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
pkey = EVP_PKEY_new();
if (!pkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("EVP_PKEY allocation failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (!keyx) {
res = true;
@@ -253,12 +359,16 @@ ec_load_key(const pgp_mpi_t &keyp, const pgp_mpi_t *keyx, pgp_curve_t curve)
x = mpi2bn(keyx);
if (!x) {
+ /* LCOV_EXCL_START */
RNP_LOG("allocation failed");
goto done;
+ /* LCOV_EXCL_END */
}
if (EC_KEY_set_private_key(ec, x) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set secret key: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
res = true;
done:
@@ -273,6 +383,7 @@ done:
pkey = NULL;
}
return pkey;
+#endif
}
rnp_result_t
@@ -296,14 +407,18 @@ ec_validate_key(const pgp_ec_key_t &key, bool secret)
rnp_result_t ret = RNP_ERROR_GENERIC;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evpkey, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
int res;
res = secret ? EVP_PKEY_check(ctx) : EVP_PKEY_public_check(ctx);
if (res < 0) {
+ /* LCOV_EXCL_START */
auto err = ERR_peek_last_error();
RNP_LOG("EC key check failed: %lu (%s)", err, ERR_reason_error_string(err));
+ /* LCOV_EXCL_END */
}
if (res > 0) {
ret = RNP_SUCCESS;
@@ -321,29 +436,61 @@ ec_write_pubkey(EVP_PKEY *pkey, pgp_mpi_t &mpi, pgp_curve_t curve)
/* EdDSA and X25519 keys are saved in a different way */
mpi.len = sizeof(mpi.mpi) - 1;
if (EVP_PKEY_get_raw_public_key(pkey, &mpi.mpi[1], &mpi.len) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed get raw public key: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
assert(mpi.len == 32);
mpi.mpi[0] = 0x40;
mpi.len++;
return true;
}
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ const ec_curve_desc_t *ec_desc = get_curve_desc(curve);
+ if (!ec_desc) {
+ return false;
+ }
+ size_t flen = BITS_TO_BYTES(ec_desc->bitlen);
+ rnp::bn qx;
+ rnp::bn qy;
+
+ /* OpenSSL before 3.0.9 by default uses compressed point for OSSL_PKEY_PARAM_PUB_KEY so use
+ * this approach */
+ bool res = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, qx.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, qy.ptr());
+ if (!res) {
+ return false;
+ }
+ /* Compose uncompressed point in mpi */
+ size_t xlen = qx.bytes();
+ size_t ylen = qy.bytes();
+ assert((xlen <= flen) && (ylen <= flen));
+ memset(mpi.mpi, 0, sizeof(mpi.mpi));
+ mpi.mpi[0] = 0x04;
+ mpi.len = 2 * flen + 1;
+ return qx.bin(&mpi.mpi[1 + flen - xlen]) && qy.bin(&mpi.mpi[1 + 2 * flen - ylen]);
+#else
const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
if (!ec) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to retrieve EC key: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
const EC_POINT *p = EC_KEY_get0_public_key(ec);
if (!p) {
+ /* LCOV_EXCL_START */
RNP_LOG("Null point: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
/* call below adds leading zeroes if needed */
mpi.len = EC_POINT_point2oct(
EC_KEY_get0_group(ec), p, POINT_CONVERSION_UNCOMPRESSED, mpi.mpi, sizeof(mpi.mpi), NULL);
if (!mpi.len) {
- RNP_LOG("Failed to encode public key: %lu", ERR_peek_last_error());
+ RNP_LOG("Failed to encode public key: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
}
return mpi.len;
+#endif
}
diff --git a/src/lib/crypto/ecdh.cpp b/src/lib/crypto/ecdh.cpp
index d4411c3..574b3b8 100644
--- a/src/lib/crypto/ecdh.cpp
+++ b/src/lib/crypto/ecdh.cpp
@@ -261,7 +261,13 @@ ecdh_encrypt_pkcs5(rnp::RNG * rng,
}
out->mlen = sizeof(out->m);
+#if defined(CRYPTO_BACKEND_BOTAN3)
+ char name[8];
+ snprintf(name, sizeof(name), "AES-%zu", 8 * kek_len);
+ if (botan_nist_kw_enc(name, 0, m, m_padded_len, kek, kek_len, out->m, &out->mlen)) {
+#else
if (botan_key_wrap3394(m, m_padded_len, kek, kek_len, out->m, &out->mlen)) {
+#endif
goto end;
}
@@ -354,8 +360,15 @@ ecdh_decrypt_pkcs5(uint8_t * out,
goto end;
}
+#if defined(CRYPTO_BACKEND_BOTAN3)
+ char name[8];
+ snprintf(name, sizeof(name), "AES-%zu", 8 * kek_len);
+ if (botan_nist_kw_dec(
+ name, 0, in->m, in->mlen, kek.data(), kek_len, deckey.data(), &deckey_len)) {
+#else
if (botan_key_unwrap3394(
in->m, in->mlen, kek.data(), kek_len, deckey.data(), &deckey_len)) {
+#endif
goto end;
}
diff --git a/src/lib/crypto/ecdh_ossl.cpp b/src/lib/crypto/ecdh_ossl.cpp
index 60b7260..5660318 100644
--- a/src/lib/crypto/ecdh_ossl.cpp
+++ b/src/lib/crypto/ecdh_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021-2023, [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -70,8 +70,10 @@ ecdh_derive_kek(uint8_t * x,
const size_t hash_len = rnp::Hash::size(key.kdf_hash_alg);
if (!hash_len) {
// must not assert here as kdf/hash algs are not checked during key parsing
+ /* LCOV_EXCL_START */
RNP_LOG("Unsupported key wrap hash algorithm.");
return RNP_ERROR_NOT_SUPPORTED;
+ /* LCOV_EXCL_END */
}
size_t other_len = kdf_other_info_serialize(
other_info, curve_desc, fingerprint, key.kdf_hash_alg, key.key_wrap_alg);
@@ -83,8 +85,10 @@ ecdh_derive_kek(uint8_t * x,
size_t reps = (kek_len + hash_len - 1) / hash_len;
// As we use AES & SHA2 we should not get more then 2 iterations
if (reps > 2) {
+ /* LCOV_EXCL_START */
RNP_LOG("Invalid key wrap/hash alg combination.");
return RNP_ERROR_NOT_SUPPORTED;
+ /* LCOV_EXCL_END */
}
size_t have = 0;
try {
@@ -100,8 +104,10 @@ ecdh_derive_kek(uint8_t * x,
}
return RNP_SUCCESS;
} catch (const std::exception &e) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to derive kek: %s", e.what());
return RNP_ERROR_GENERIC;
+ /* LCOV_EXCL_END */
}
}
@@ -115,27 +121,35 @@ ecdh_rfc3394_wrap_ctx(EVP_CIPHER_CTX **ctx,
const char *cipher_name = NULL;
ARRAY_LOOKUP_BY_ID(ecdh_wrap_alg_map, alg, name, wrap_alg, cipher_name);
if (!cipher_name) {
+ /* LCOV_EXCL_START */
RNP_LOG("Unsupported key wrap algorithm: %d", (int) wrap_alg);
return RNP_ERROR_NOT_SUPPORTED;
+ /* LCOV_EXCL_END */
}
const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_name);
if (!cipher) {
+ /* LCOV_EXCL_START */
RNP_LOG("Cipher %s is not supported by OpenSSL.", cipher_name);
return RNP_ERROR_NOT_SUPPORTED;
+ /* LCOV_EXCL_END */
}
*ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed : %lu", ERR_peek_last_error());
return RNP_ERROR_OUT_OF_MEMORY;
+ /* LCOV_EXCL_END */
}
EVP_CIPHER_CTX_set_flags(*ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
int res = decrypt ? EVP_DecryptInit_ex(*ctx, cipher, NULL, key, NULL) :
EVP_EncryptInit_ex(*ctx, cipher, NULL, key, NULL);
if (res <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to initialize cipher : %lu", ERR_peek_last_error());
EVP_CIPHER_CTX_free(*ctx);
*ctx = NULL;
return RNP_ERROR_GENERIC;
+ /* LCOV_EXCL_END */
}
return RNP_SUCCESS;
}
@@ -151,14 +165,16 @@ ecdh_rfc3394_wrap(uint8_t * out,
EVP_CIPHER_CTX *ctx = NULL;
rnp_result_t ret = ecdh_rfc3394_wrap_ctx(&ctx, wrap_alg, key, false);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Wrap context initialization failed.");
return ret;
+ /* LCOV_EXCL_END */
}
int intlen = *out_len;
/* encrypts in one pass, no final is needed */
int res = EVP_EncryptUpdate(ctx, out, &intlen, in, in_len);
if (res <= 0) {
- RNP_LOG("Failed to encrypt data : %lu", ERR_peek_last_error());
+ RNP_LOG("Failed to encrypt data : %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
} else {
*out_len = intlen;
}
@@ -181,8 +197,10 @@ ecdh_rfc3394_unwrap(uint8_t * out,
EVP_CIPHER_CTX *ctx = NULL;
rnp_result_t ret = ecdh_rfc3394_wrap_ctx(&ctx, wrap_alg, key, true);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Unwrap context initialization failed.");
return ret;
+ /* LCOV_EXCL_END */
}
int intlen = *out_len;
/* decrypts in one pass, no final is needed */
@@ -201,21 +219,29 @@ ecdh_derive_secret(EVP_PKEY *sec, EVP_PKEY *peer, uint8_t *x, size_t *xlen)
{
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(sec, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
bool res = false;
if (EVP_PKEY_derive_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Key derivation init failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_derive_set_peer(ctx, peer) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Peer setting failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_derive(ctx, x, xlen) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to obtain shared secret size: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
res = true;
done:
@@ -256,14 +282,18 @@ ecdh_encrypt_pkcs5(rnp::RNG * rng,
/* check whether we have valid wrap_alg before doing heavy operations */
size_t keklen = ecdh_kek_len(key->key_wrap_alg);
if (!keklen) {
+ /* LCOV_EXCL_START */
RNP_LOG("Unsupported key wrap algorithm: %d", (int) key->key_wrap_alg);
return RNP_ERROR_NOT_SUPPORTED;
+ /* LCOV_EXCL_END */
}
/* load our public key */
EVP_PKEY *pkey = ec_load_key(key->p, NULL, key->curve);
if (!pkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to load public key.");
return RNP_ERROR_BAD_PARAMETERS;
+ /* LCOV_EXCL_END */
}
rnp::secure_array<uint8_t, MAX_CURVE_BYTELEN + 1> sec;
rnp::secure_array<uint8_t, MAX_AES_KEY_SIZE> kek;
@@ -274,28 +304,36 @@ ecdh_encrypt_pkcs5(rnp::RNG * rng,
/* generate ephemeral key */
EVP_PKEY *ephkey = ec_generate_pkey(PGP_PKA_ECDH, key->curve);
if (!ephkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to generate ephemeral key.");
ret = RNP_ERROR_KEY_GENERATION;
goto done;
+ /* LCOV_EXCL_END */
}
/* do ECDH derivation */
if (!ecdh_derive_secret(ephkey, pkey, sec.data(), &seclen)) {
+ /* LCOV_EXCL_START */
RNP_LOG("ECDH derivation failed.");
goto done;
+ /* LCOV_EXCL_END */
}
/* here we got x value in sec, deriving kek */
ret = ecdh_derive_kek(sec.data(), seclen, *key, fingerprint, kek.data(), keklen);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to derive KEK.");
goto done;
+ /* LCOV_EXCL_END */
}
/* add PKCS#7 padding */
size_t m_padded_len;
m_padded_len = ((in_len / 8) + 1) * 8;
memcpy(mpad.data(), in, in_len);
if (!pad_pkcs7(mpad.data(), m_padded_len, in_len)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to add PKCS #7 padding.");
goto done;
+ /* LCOV_EXCL_END */
}
/* do RFC 3394 AES key wrap */
static_assert(sizeof(out->m) == ECDH_WRAPPED_KEY_SIZE, "Wrong ECDH wrapped key size.");
@@ -303,13 +341,17 @@ ecdh_encrypt_pkcs5(rnp::RNG * rng,
ret = ecdh_rfc3394_wrap(
out->m, &out->mlen, mpad.data(), m_padded_len, kek.data(), key->key_wrap_alg);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to wrap key.");
goto done;
+ /* LCOV_EXCL_END */
}
/* write ephemeral public key */
if (!ec_write_pubkey(ephkey, out->p, key->curve)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to write ec key.");
goto done;
+ /* LCOV_EXCL_END */
}
ret = RNP_SUCCESS;
done:
@@ -351,32 +393,42 @@ ecdh_decrypt_pkcs5(uint8_t * out,
rnp_result_t ret = RNP_ERROR_GENERIC;
EVP_PKEY * pkey = ec_load_key(key->p, &key->x, key->curve);
if (!pkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to load secret key.");
ret = RNP_ERROR_BAD_PARAMETERS;
goto done;
+ /* LCOV_EXCL_END */
}
/* do ECDH derivation */
if (!ecdh_derive_secret(pkey, ephkey, sec.data(), &seclen)) {
+ /* LCOV_EXCL_START */
RNP_LOG("ECDH derivation failed.");
goto done;
+ /* LCOV_EXCL_END */
}
/* here we got x value in sec, deriving kek */
ret = ecdh_derive_kek(sec.data(), seclen, *key, fingerprint, kek.data(), keklen);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to derive KEK.");
goto done;
+ /* LCOV_EXCL_END */
}
/* do RFC 3394 AES key unwrap */
ret = ecdh_rfc3394_unwrap(
mpad.data(), &mpadlen, in->m, in->mlen, kek.data(), key->key_wrap_alg);
if (ret) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to unwrap key.");
goto done;
+ /* LCOV_EXCL_END */
}
/* remove PKCS#7 padding */
if (!unpad_pkcs7(mpad.data(), mpadlen, &mpadlen)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to unpad key.");
goto done;
+ /* LCOV_EXCL_END */
}
assert(mpadlen <= *out_len);
*out_len = mpadlen;
diff --git a/src/lib/crypto/elgamal_ossl.cpp b/src/lib/crypto/elgamal_ossl.cpp
index f3fa381..6049ad2 100644
--- a/src/lib/crypto/elgamal_ossl.cpp
+++ b/src/lib/crypto/elgamal_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021, 2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -38,6 +38,10 @@
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#endif
// Max supported key byte size
#define ELGAMAL_MAX_P_BYTELEN BITS_TO_BYTES(PGP_MPINT_BITS)
@@ -47,8 +51,10 @@ elgamal_validate_key(const pgp_eg_key_t *key, bool secret)
{
BN_CTX *ctx = BN_CTX_new();
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed.");
return false;
+ /* LCOV_EXCL_END */
}
BN_CTX_start(ctx);
bool res = false;
@@ -86,8 +92,10 @@ elgamal_validate_key(const pgp_eg_key_t *key, bool secret)
}
for (size_t i = 2; i < (1 << 17); i++) {
if (!BN_mod_mul_reciprocal(r, r, g, rctx, ctx)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Multiplication failed.");
goto done;
+ /* LCOV_EXCL_END */
}
if (BN_cmp(r, BN_value_one()) == 0) {
RNP_LOG("Small subgroup detected. Order %zu", i);
@@ -135,8 +143,10 @@ pkcs1v15_pad(uint8_t *out, size_t out_len, const uint8_t *in, size_t in_len)
while (!out[i] && (cntr--) && (RAND_bytes(&out[i], 1) == 1)) {
}
if (!out[i]) {
+ /* LCOV_EXCL_START */
RNP_LOG("Something is wrong with RNG.");
return false;
+ /* LCOV_EXCL_END */
}
}
memcpy(out + rnd + 3, in, in_len);
@@ -176,14 +186,18 @@ elgamal_encrypt_pkcs1(rnp::RNG * rng,
pgp_mpi_t mm = {};
mm.len = key->p.len;
if (!pkcs1v15_pad(mm.mpi, mm.len, in, in_len)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to add PKCS1 v1.5 padding.");
return RNP_ERROR_BAD_PARAMETERS;
+ /* LCOV_EXCL_END */
}
rnp_result_t ret = RNP_ERROR_GENERIC;
BN_CTX * ctx = BN_CTX_new();
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed.");
return RNP_ERROR_OUT_OF_MEMORY;
+ /* LCOV_EXCL_END */
}
BN_CTX_start(ctx);
BN_MONT_CTX *mctx = BN_MONT_CTX_new();
@@ -195,41 +209,55 @@ elgamal_encrypt_pkcs1(rnp::RNG * rng,
bignum_t * c2 = BN_CTX_get(ctx);
bignum_t * k = BN_secure_new();
if (!mctx || !m || !p || !g || !y || !c1 || !c2 || !k) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed.");
ret = RNP_ERROR_OUT_OF_MEMORY;
goto done;
+ /* LCOV_EXCL_END */
}
/* initialize Montgomery context */
if (BN_MONT_CTX_set(mctx, p, ctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to setup Montgomery context.");
goto done;
+ /* LCOV_EXCL_END */
}
int res;
/* must not fail */
res = BN_rshift1(c1, p);
assert(res == 1);
if (res < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("BN_rshift1 failed.");
goto done;
+ /* LCOV_EXCL_END */
}
/* generate k */
if (BN_rand_range(k, c1) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to generate k.");
goto done;
+ /* LCOV_EXCL_END */
}
/* calculate c1 = g ^ k (mod p) */
if (BN_mod_exp_mont_consttime(c1, g, k, p, ctx, mctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Exponentiation 1 failed");
goto done;
+ /* LCOV_EXCL_END */
}
/* calculate c2 = m * y ^ k (mod p)*/
if (BN_mod_exp_mont_consttime(c2, y, k, p, ctx, mctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Exponentiation 2 failed");
goto done;
+ /* LCOV_EXCL_END */
}
if (BN_mod_mul(c2, c2, m, p, ctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Multiplication failed");
goto done;
+ /* LCOV_EXCL_END */
}
res = bn2mpi(c1, &out->g) && bn2mpi(c2, &out->m);
assert(res == 1);
@@ -258,8 +286,10 @@ elgamal_decrypt_pkcs1(rnp::RNG * rng,
}
BN_CTX *ctx = BN_CTX_new();
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed.");
return RNP_ERROR_OUT_OF_MEMORY;
+ /* LCOV_EXCL_END */
}
pgp_mpi_t mm = {};
size_t padlen = 0;
@@ -274,37 +304,49 @@ elgamal_decrypt_pkcs1(rnp::RNG * rng,
bignum_t * s = BN_CTX_get(ctx);
bignum_t * m = BN_secure_new();
if (!mctx || !p || !g || !x || !c1 || !c2 || !m) {
+ /* LCOV_EXCL_START */
RNP_LOG("Allocation failed.");
ret = RNP_ERROR_OUT_OF_MEMORY;
goto done;
+ /* LCOV_EXCL_END */
}
/* initialize Montgomery context */
if (BN_MONT_CTX_set(mctx, p, ctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to setup Montgomery context.");
goto done;
+ /* LCOV_EXCL_END */
}
/* calculate s = c1 ^ x (mod p) */
if (BN_mod_exp_mont_consttime(s, c1, x, p, ctx, mctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Exponentiation 1 failed");
goto done;
+ /* LCOV_EXCL_END */
}
/* calculate s^-1 (mod p) */
BN_set_flags(s, BN_FLG_CONSTTIME);
if (!BN_mod_inverse(s, s, p, ctx)) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to calculate inverse.");
goto done;
+ /* LCOV_EXCL_END */
}
/* calculate m = c2 * s ^ -1 (mod p)*/
if (BN_mod_mul(m, c2, s, p, ctx) < 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Multiplication failed");
goto done;
+ /* LCOV_EXCL_END */
}
bool res;
res = bn2mpi(m, &mm);
assert(res);
if (!res) {
+ /* LCOV_EXCL_START */
RNP_LOG("bn2mpi failed.");
goto done;
+ /* LCOV_EXCL_END */
}
/* unpad, handling skipped leftmost 0 case */
if (!pkcs1v15_unpad(&padlen, mm.mpi, mm.len, mm.len == key->p.len - 1)) {
@@ -334,8 +376,10 @@ elgamal_generate(rnp::RNG *rng, pgp_eg_key_t *key, size_t keybits)
return RNP_ERROR_BAD_PARAMETERS;
}
- rnp_result_t ret = RNP_ERROR_GENERIC;
- const DH * dh = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+#if !defined(CRYPTO_BACKEND_OPENSSL3)
+ const DH *dh = NULL;
+#endif
EVP_PKEY * pkey = NULL;
EVP_PKEY * parmkey = NULL;
EVP_PKEY_CTX *ctx = NULL;
@@ -343,47 +387,93 @@ elgamal_generate(rnp::RNG *rng, pgp_eg_key_t *key, size_t keybits)
/* Generate DH params, which usable for ElGamal as well */
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DH, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
return ret;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_paramgen_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, keybits) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set key bits: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
/* OpenSSL correctly handles case with g = 5, making sure that g is primitive root of
* q-group */
if (EVP_PKEY_CTX_set_dh_paramgen_generator(ctx, DH_GENERATOR_5) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set key generator: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_paramgen(ctx, &parmkey) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to generate parameters: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
EVP_PKEY_CTX_free(ctx);
/* Generate DH (ElGamal) key */
start:
ctx = EVP_PKEY_CTX_new(parmkey, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create ctx: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_keygen_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init keygen: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("ElGamal keygen failed: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
+ }
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ {
+ rnp::bn y;
+ if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, y.ptr())) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("Failed to retrieve ElGamal public key: %lu", ERR_peek_last_error());
+ goto done;
+ /* LCOV_EXCL_END */
+ }
+ if (y.bytes() != BITS_TO_BYTES(keybits)) {
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto start;
+ }
+
+ rnp::bn p;
+ rnp::bn g;
+ rnp::bn x;
+ bool res = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_P, p.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_G, g.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, x.ptr());
+ if (res && p.mpi(key->p) && g.mpi(key->g) && y.mpi(key->y) && x.mpi(key->x)) {
+ ret = RNP_SUCCESS;
+ }
}
+#else
dh = EVP_PKEY_get0_DH(pkey);
if (!dh) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to retrieve DH key: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
if (BITS_TO_BYTES(BN_num_bits(DH_get0_pub_key(dh))) != BITS_TO_BYTES(keybits)) {
EVP_PKEY_CTX_free(ctx);
@@ -402,14 +492,17 @@ start:
y = DH_get0_pub_key(dh);
x = DH_get0_priv_key(dh);
if (!p || !g || !y || !x) {
+ /* LCOV_EXCL_START */
ret = RNP_ERROR_BAD_STATE;
goto done;
+ /* LCOV_EXCL_END */
}
bn2mpi(p, &key->p);
bn2mpi(g, &key->g);
bn2mpi(y, &key->y);
bn2mpi(x, &key->x);
ret = RNP_SUCCESS;
+#endif
done:
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(parmkey);
diff --git a/src/lib/crypto/rsa.cpp b/src/lib/crypto/rsa.cpp
index f7ddefe..83fa044 100644
--- a/src/lib/crypto/rsa.cpp
+++ b/src/lib/crypto/rsa.cpp
@@ -333,12 +333,16 @@ rsa_decrypt_pkcs1(rnp::RNG * rng,
return RNP_ERROR_OUT_OF_MEMORY;
}
+ size_t skip = 0;
if (botan_pk_op_decrypt_create(&decrypt_op, rsa_key, "PKCS1v15", 0)) {
goto done;
}
-
+ /* Skip trailing zeroes if any as Botan3 doesn't like m.len > e.len */
+ while ((in->m.len - skip > key->e.len) && !in->m.mpi[skip]) {
+ skip++;
+ }
*out_len = PGP_MPINT_SIZE;
- if (botan_pk_op_decrypt(decrypt_op, out, out_len, in->m.mpi, in->m.len)) {
+ if (botan_pk_op_decrypt(decrypt_op, out, out_len, in->m.mpi + skip, in->m.len - skip)) {
goto done;
}
ret = RNP_SUCCESS;
diff --git a/src/lib/crypto/rsa_ossl.cpp b/src/lib/crypto/rsa_ossl.cpp
index 24cff29..445974c 100644
--- a/src/lib/crypto/rsa_ossl.cpp
+++ b/src/lib/crypto/rsa_ossl.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2022, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2021-2023, [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -45,30 +45,29 @@
static RSA *
rsa_load_public_key(const pgp_rsa_key_t *key)
{
- RSA * rsa = NULL;
- bignum_t *n = mpi2bn(&key->n);
- bignum_t *e = mpi2bn(&key->e);
+ rnp::bn n(key->n);
+ rnp::bn e(key->e);
- if (!n || !e) {
+ if (!n.get() || !e.get()) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- rsa = RSA_new();
+ RSA *rsa = RSA_new();
if (!rsa) {
+ /* LCOV_EXCL_START */
RNP_LOG("Out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- if (RSA_set0_key(rsa, n, e, NULL) != 1) {
+ /* OpenSSL set0 function transfers ownership of bignums */
+ if (RSA_set0_key(rsa, n.own(), e.own(), NULL) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Public key load error: %lu", ERR_peek_last_error());
RSA_free(rsa);
- rsa = NULL;
- goto done;
- }
-done:
- /* OpenSSL set0 function transfers ownership of bignums */
- if (!rsa) {
- bn_free(n);
- bn_free(e);
+ return NULL;
+ /* LCOV_EXCL_END */
}
return rsa;
}
@@ -76,44 +75,41 @@ done:
static RSA *
rsa_load_secret_key(const pgp_rsa_key_t *key)
{
- RSA * rsa = NULL;
- bignum_t *n = mpi2bn(&key->n);
- bignum_t *e = mpi2bn(&key->e);
- bignum_t *p = mpi2bn(&key->p);
- bignum_t *q = mpi2bn(&key->q);
- bignum_t *d = mpi2bn(&key->d);
+ rnp::bn n(key->n);
+ rnp::bn e(key->e);
+ rnp::bn p(key->p);
+ rnp::bn q(key->q);
+ rnp::bn d(key->d);
- if (!n || !p || !q || !e || !d) {
+ if (!n.get() || !p.get() || !q.get() || !e.get() || !d.get()) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- rsa = RSA_new();
+ RSA *rsa = RSA_new();
if (!rsa) {
+ /* LCOV_EXCL_START */
RNP_LOG("Out of memory");
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
- if (RSA_set0_key(rsa, n, e, d) != 1) {
+ /* OpenSSL set0 function transfers ownership of bignums */
+ if (RSA_set0_key(rsa, n.own(), e.own(), d.own()) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Secret key load error: %lu", ERR_peek_last_error());
RSA_free(rsa);
- rsa = NULL;
- goto done;
+ return NULL;
+ /* LCOV_EXCL_END */
}
/* OpenSSL has p < q, as we do */
- if (RSA_set0_factors(rsa, p, q) != 1) {
+ if (RSA_set0_factors(rsa, p.own(), q.own()) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Factors load error: %lu", ERR_peek_last_error());
RSA_free(rsa);
- rsa = NULL;
- goto done;
- }
-done:
- /* OpenSSL set0 function transfers ownership of bignums */
- if (!rsa) {
- bn_free(n);
- bn_free(p);
- bn_free(q);
- bn_free(e);
- bn_free(d);
+ return NULL;
+ /* LCOV_EXCL_END */
}
return rsa;
}
@@ -123,8 +119,10 @@ rsa_init_context(const pgp_rsa_key_t *key, bool secret)
{
EVP_PKEY *evpkey = EVP_PKEY_new();
if (!evpkey) {
+ /* LCOV_EXCL_START */
RNP_LOG("allocation failed");
return NULL;
+ /* LCOV_EXCL_END */
}
EVP_PKEY_CTX *ctx = NULL;
RSA * rsakey = secret ? rsa_load_secret_key(key) : rsa_load_public_key(key);
@@ -132,12 +130,14 @@ rsa_init_context(const pgp_rsa_key_t *key, bool secret)
goto done;
}
if (EVP_PKEY_set1_RSA(evpkey, rsakey) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set key: %lu", ERR_peek_last_error());
goto done;
+ /* LCOV_EXCL_END */
}
ctx = EVP_PKEY_CTX_new(evpkey, NULL);
if (!ctx) {
- RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error());
+ RNP_LOG("Context allocation failed: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
}
done:
RSA_free(rsakey);
@@ -150,70 +150,72 @@ rsa_bld_params(const pgp_rsa_key_t *key, bool secret)
{
OSSL_PARAM * params = NULL;
OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
- bignum_t * n = mpi2bn(&key->n);
- bignum_t * e = mpi2bn(&key->e);
- bignum_t * d = NULL;
- bignum_t * p = NULL;
- bignum_t * q = NULL;
- bignum_t * u = NULL;
+ rnp::bn n(key->n);
+ rnp::bn e(key->e);
+ rnp::bn d;
+ rnp::bn p;
+ rnp::bn q;
+ rnp::bn u;
BN_CTX * bnctx = NULL;
- if (!n || !e || !bld) {
+ if (!n.get() || !e.get() || !bld) {
+ /* LCOV_EXCL_START */
RNP_LOG("Out of memory");
goto done;
+ /* LCOV_EXCL_END */
}
- if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n) ||
- !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e)) {
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n.get()) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e.get())) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to push RSA params.");
- goto done;
+ OSSL_PARAM_BLD_free(bld);
+ return NULL;
+ /* LCOV_EXCL_END */
}
if (secret) {
- d = mpi2bn(&key->d);
+ d.set(key->d);
/* As we have u = p^-1 mod q, and qInv = q^-1 mod p, we need to replace one with
* another */
- p = mpi2bn(&key->q);
- q = mpi2bn(&key->p);
- u = mpi2bn(&key->u);
- if (!d || !p || !q || !u) {
+ p.set(key->q);
+ q.set(key->p);
+ u.set(key->u);
+ if (!d.get() || !p.get() || !q.get() || !u.get()) {
goto done;
}
/* We need to calculate exponents manually */
bnctx = BN_CTX_new();
if (!bnctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to allocate BN_CTX.");
goto done;
+ /* LCOV_EXCL_END */
}
bignum_t *p1 = BN_CTX_get(bnctx);
bignum_t *q1 = BN_CTX_get(bnctx);
bignum_t *dp = BN_CTX_get(bnctx);
bignum_t *dq = BN_CTX_get(bnctx);
- if (!BN_copy(p1, p) || !BN_sub_word(p1, 1) || !BN_copy(q1, q) || !BN_sub_word(q1, 1) ||
- !BN_mod(dp, d, p1, bnctx) || !BN_mod(dq, d, q1, bnctx)) {
- RNP_LOG("Failed to calculate dP or dQ.");
+ if (!BN_copy(p1, p.get()) || !BN_sub_word(p1, 1) || !BN_copy(q1, q.get()) ||
+ !BN_sub_word(q1, 1) || !BN_mod(dp, d.get(), p1, bnctx) ||
+ !BN_mod(dq, d.get(), q1, bnctx)) {
+ RNP_LOG("Failed to calculate dP or dQ."); // LCOV_EXCL_LINE
}
/* Push params */
- if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, d) ||
- !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1, p) ||
- !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2, q) ||
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, d.get()) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1, p.get()) ||
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2, q.get()) ||
!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT1, dp) ||
!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT2, dq) ||
- !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, u)) {
- RNP_LOG("Failed to push RSA secret params.");
+ !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, u.get())) {
+ RNP_LOG("Failed to push RSA secret params."); // LCOV_EXCL_LINE
goto done;
}
}
params = OSSL_PARAM_BLD_to_param(bld);
if (!params) {
- RNP_LOG("Failed to build RSA params: %s.", ossl_latest_err());
+ RNP_LOG("Failed to build RSA params: %s.", ossl_latest_err()); // LCOV_EXCL_LINE
}
done:
- bn_free(n);
- bn_free(e);
- bn_free(d);
- bn_free(p);
- bn_free(q);
- bn_free(u);
BN_CTX_free(bnctx);
OSSL_PARAM_BLD_free(bld);
return params;
@@ -231,17 +233,21 @@ rsa_load_key(const pgp_rsa_key_t *key, bool secret)
EVP_PKEY * res = NULL;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Context allocation failed: %s", ossl_latest_err());
goto done;
+ /* LCOV_EXCL_END */
}
/* Create key */
if (EVP_PKEY_fromdata_init(ctx) <= 0) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to initialize key creation: %s", ossl_latest_err());
goto done;
+ /* LCOV_EXCL_END */
}
if (EVP_PKEY_fromdata(
ctx, &res, secret ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY, params) <= 0) {
- RNP_LOG("Failed to create RSA key: %s", ossl_latest_err());
+ RNP_LOG("Failed to create RSA key: %s", ossl_latest_err()); // LCOV_EXCL_LINE
}
done:
EVP_PKEY_CTX_free(ctx);
@@ -258,7 +264,7 @@ rsa_init_context(const pgp_rsa_key_t *key, bool secret)
}
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx) {
- RNP_LOG("Context allocation failed: %s", ossl_latest_err());
+ RNP_LOG("Context allocation failed: %s", ossl_latest_err()); // LCOV_EXCL_LINE
}
EVP_PKEY_free(pkey);
return ctx;
@@ -271,12 +277,14 @@ rsa_validate_key(rnp::RNG *rng, const pgp_rsa_key_t *key, bool secret)
#ifdef CRYPTO_BACKEND_OPENSSL3
EVP_PKEY_CTX *ctx = rsa_init_context(key, secret);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init context: %s", ossl_latest_err());
return RNP_ERROR_GENERIC;
+ /* LCOV_EXCL_END */
}
int res = secret ? EVP_PKEY_pairwise_check(ctx) : EVP_PKEY_public_check(ctx);
if (res <= 0) {
- RNP_LOG("Key validation error: %s", ossl_latest_err());
+ RNP_LOG("Key validation error: %s", ossl_latest_err()); // LCOV_EXCL_LINE
}
EVP_PKEY_CTX_free(ctx);
return res > 0 ? RNP_SUCCESS : RNP_ERROR_GENERIC;
@@ -284,34 +292,33 @@ rsa_validate_key(rnp::RNG *rng, const pgp_rsa_key_t *key, bool secret)
if (secret) {
EVP_PKEY_CTX *ctx = rsa_init_context(key, secret);
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to init context: %s", ossl_latest_err());
return RNP_ERROR_GENERIC;
+ /* LCOV_EXCL_END */
}
int res = EVP_PKEY_check(ctx);
if (res <= 0) {
- RNP_LOG("Key validation error: %s", ossl_latest_err());
+ RNP_LOG("Key validation error: %s", ossl_latest_err()); // LCOV_EXCL_LINE
}
EVP_PKEY_CTX_free(ctx);
return res > 0 ? RNP_SUCCESS : RNP_ERROR_GENERIC;
}
/* OpenSSL 1.1.1 doesn't have RSA public key check function, so let's do some checks */
- rnp_result_t ret = RNP_ERROR_GENERIC;
- bignum_t * n = mpi2bn(&key->n);
- bignum_t * e = mpi2bn(&key->e);
- if (!n || !e) {
+ rnp::bn n(key->n);
+ rnp::bn e(key->e);
+ if (!n.get() || !e.get()) {
+ /* LCOV_EXCL_START */
RNP_LOG("out of memory");
- ret = RNP_ERROR_OUT_OF_MEMORY;
- goto done;
+ return RNP_ERROR_OUT_OF_MEMORY;
+ /* LCOV_EXCL_END */
}
- if ((BN_num_bits(n) < 512) || !BN_is_odd(n) || (BN_num_bits(e) < 2) || !BN_is_odd(e)) {
- goto done;
+ if ((BN_num_bits(n.get()) < 512) || !BN_is_odd(n.get()) || (BN_num_bits(e.get()) < 2) ||
+ !BN_is_odd(e.get())) {
+ return RNP_ERROR_GENERIC;
}
- ret = RNP_SUCCESS;
-done:
- bn_free(n);
- bn_free(e);
- return ret;
+ return RNP_SUCCESS;
#endif
}
@@ -528,6 +535,97 @@ done:
return ret;
}
+static bool
+rsa_calculate_pqu(const bignum_t *p, const bignum_t *q, const bignum_t *u, pgp_rsa_key_t &key)
+{
+ /* OpenSSL doesn't care whether p < q */
+ if (BN_cmp(p, q) > 0) {
+ /* In this case we have u, as iqmp is inverse of q mod p, and we exchange them */
+ bn2mpi(q, &key.p);
+ bn2mpi(p, &key.q);
+ bn2mpi(u, &key.u);
+ return true;
+ }
+
+ BN_CTX *bnctx = BN_CTX_new();
+ if (!bnctx) {
+ return false;
+ }
+
+ /* we need to calculate u, since we need inverse of p mod q, while OpenSSL has inverse of q
+ * mod p, and doesn't care of p < q */
+ BN_CTX_start(bnctx);
+ bignum_t *nu = BN_CTX_get(bnctx);
+ bignum_t *nq = BN_CTX_get(bnctx);
+ if (!nu || !nq) {
+ BN_CTX_free(bnctx);
+ return false;
+ }
+ BN_with_flags(nq, q, BN_FLG_CONSTTIME);
+ /* calculate inverse of p mod q */
+ if (!BN_mod_inverse(nu, p, nq, bnctx)) {
+ /* LCOV_EXCL_START */
+ RNP_LOG("Failed to calculate u");
+ BN_CTX_free(bnctx);
+ return false;
+ /* LCOV_EXCL_END */
+ }
+ bn2mpi(p, &key.p);
+ bn2mpi(q, &key.q);
+ bn2mpi(nu, &key.u);
+ BN_CTX_free(bnctx);
+ return true;
+}
+
+static bool
+rsa_extract_key(EVP_PKEY *pkey, pgp_rsa_key_t &key)
+{
+#if defined(CRYPTO_BACKEND_OPENSSL3)
+ rnp::bn n;
+ rnp::bn e;
+ rnp::bn d;
+ rnp::bn p;
+ rnp::bn q;
+ rnp::bn u;
+
+ bool res = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, n.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, e.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_D, d.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, p.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, q.ptr()) &&
+ EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, u.ptr()) &&
+ rsa_calculate_pqu(p.get(), q.get(), u.get(), key);
+ return res && n.mpi(key.n) && e.mpi(key.e) && d.mpi(key.d);
+#else
+ const RSA *rsa = EVP_PKEY_get0_RSA(pkey);
+ if (!rsa) {
+ RNP_LOG("Failed to retrieve RSA key: %lu", ERR_peek_last_error());
+ return false;
+ }
+ if (RSA_check_key(rsa) != 1) {
+ RNP_LOG("Key validation error: %lu", ERR_peek_last_error());
+ return false;
+ }
+
+ const bignum_t *n = RSA_get0_n(rsa);
+ const bignum_t *e = RSA_get0_e(rsa);
+ const bignum_t *d = RSA_get0_d(rsa);
+ const bignum_t *p = RSA_get0_p(rsa);
+ const bignum_t *q = RSA_get0_q(rsa);
+ const bignum_t *u = RSA_get0_iqmp(rsa);
+ if (!n || !e || !d || !p || !q || !u) {
+ return false;
+ }
+ if (!rsa_calculate_pqu(p, q, u, key)) {
+ return false;
+ }
+ bn2mpi(n, &key.n);
+ bn2mpi(e, &key.e);
+ bn2mpi(d, &key.d);
+ return true;
+#endif
+}
+
rnp_result_t
rsa_generate(rnp::RNG *rng, pgp_rsa_key_t *key, size_t numbits)
{
@@ -535,12 +633,9 @@ rsa_generate(rnp::RNG *rng, pgp_rsa_key_t *key, size_t numbits)
return RNP_ERROR_BAD_PARAMETERS;
}
- rnp_result_t ret = RNP_ERROR_GENERIC;
- const RSA * rsa = NULL;
- EVP_PKEY * pkey = NULL;
- EVP_PKEY_CTX * ctx = NULL;
- const bignum_t *u = NULL;
- BN_CTX * bnctx = NULL;
+ rnp_result_t ret = RNP_ERROR_GENERIC;
+ EVP_PKEY * pkey = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (!ctx) {
@@ -559,71 +654,11 @@ rsa_generate(rnp::RNG *rng, pgp_rsa_key_t *key, size_t numbits)
RNP_LOG("RSA keygen failed: %lu", ERR_peek_last_error());
goto done;
}
- rsa = EVP_PKEY_get0_RSA(pkey);
- if (!rsa) {
- RNP_LOG("Failed to retrieve RSA key: %lu", ERR_peek_last_error());
- goto done;
- }
- if (RSA_check_key(rsa) != 1) {
- RNP_LOG("Key validation error: %lu", ERR_peek_last_error());
- goto done;
- }
-
- const bignum_t *n;
- const bignum_t *e;
- const bignum_t *p;
- const bignum_t *q;
- const bignum_t *d;
- n = RSA_get0_n(rsa);
- e = RSA_get0_e(rsa);
- d = RSA_get0_d(rsa);
- p = RSA_get0_p(rsa);
- q = RSA_get0_q(rsa);
- if (!n || !e || !d || !p || !q) {
- ret = RNP_ERROR_OUT_OF_MEMORY;
- goto done;
+ if (rsa_extract_key(pkey, *key)) {
+ ret = RNP_SUCCESS;
}
- /* OpenSSL doesn't care whether p < q */
- if (BN_cmp(p, q) > 0) {
- /* In this case we have u, as iqmp is inverse of q mod p, and we exchange them */
- const bignum_t *tmp = p;
- p = q;
- q = tmp;
- u = RSA_get0_iqmp(rsa);
- } else {
- /* we need to calculate u, since we need inverse of p mod q, while OpenSSL has inverse
- * of q mod p, and doesn't care of p < q */
- bnctx = BN_CTX_new();
- if (!bnctx) {
- ret = RNP_ERROR_OUT_OF_MEMORY;
- goto done;
- }
- BN_CTX_start(bnctx);
- bignum_t *nu = BN_CTX_get(bnctx);
- bignum_t *nq = BN_CTX_get(bnctx);
- if (!nu || !nq) {
- ret = RNP_ERROR_OUT_OF_MEMORY;
- goto done;
- }
- BN_with_flags(nq, q, BN_FLG_CONSTTIME);
- /* calculate inverse of p mod q */
- if (!BN_mod_inverse(nu, p, nq, bnctx)) {
- RNP_LOG("Failed to calculate u");
- ret = RNP_ERROR_BAD_STATE;
- goto done;
- }
- u = nu;
- }
- bn2mpi(n, &key->n);
- bn2mpi(e, &key->e);
- bn2mpi(p, &key->p);
- bn2mpi(q, &key->q);
- bn2mpi(d, &key->d);
- bn2mpi(u, &key->u);
- ret = RNP_SUCCESS;
done:
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey);
- BN_CTX_free(bnctx);
return ret;
}
diff --git a/src/lib/crypto/symmetric.cpp b/src/lib/crypto/symmetric.cpp
index aeed784..0f7b259 100644
--- a/src/lib/crypto/symmetric.cpp
+++ b/src/lib/crypto/symmetric.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023, [Ribose Inc](https://www.ribose.com).
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
@@ -57,6 +57,7 @@
#include <stdlib.h>
#include <assert.h>
#include <botan/ffi.h>
+#include <botan/build.h>
#include "utils.h"
static const char *
@@ -224,7 +225,7 @@ pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
uint64_t buf64[512]; // 4KB - page size
uint64_t iv64[2];
size_t blocks, blockb;
- unsigned blsize = crypt->blocksize;
+ size_t blsize = crypt->blocksize;
/* encrypting till the block boundary */
while (bytes && crypt->cfb.remaining) {
@@ -306,7 +307,7 @@ pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
uint64_t outbuf64[512];
uint64_t iv64[2];
size_t blocks, blockb;
- unsigned blsize = crypt->blocksize;
+ size_t blsize = crypt->blocksize;
/* decrypting till the block boundary */
while (bytes && crypt->cfb.remaining) {
@@ -613,7 +614,10 @@ pgp_cipher_aead_finish(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
void
pgp_cipher_aead_destroy(pgp_crypt_t *crypt)
{
- botan_cipher_destroy(crypt->aead.obj);
+ if (crypt->aead.obj) {
+ botan_cipher_destroy(crypt->aead.obj);
+ }
+ memset(crypt, 0x0, sizeof(*crypt));
}
size_t
diff --git a/src/lib/crypto/symmetric_ossl.cpp b/src/lib/crypto/symmetric_ossl.cpp
index 98e90ed..ff19362 100644
--- a/src/lib/crypto/symmetric_ossl.cpp
+++ b/src/lib/crypto/symmetric_ossl.cpp
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2021 Ribose Inc.
+ * Copyright (c) 2021-2023 Ribose Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -94,8 +94,10 @@ pgp_cipher_cfb_start(pgp_crypt_t * crypt,
const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_name);
if (!cipher) {
+ /* LCOV_EXCL_START */
RNP_LOG("Cipher %s is not supported by OpenSSL.", cipher_name);
return false;
+ /* LCOV_EXCL_END */
}
crypt->alg = alg;
@@ -104,9 +106,11 @@ pgp_cipher_cfb_start(pgp_crypt_t * crypt,
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
int res = EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv);
if (res != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to initialize cipher.");
EVP_CIPHER_CTX_free(ctx);
return false;
+ /* LCOV_EXCL_END */
}
crypt->cfb.obj = ctx;
@@ -131,7 +135,7 @@ int
pgp_cipher_cfb_finish(pgp_crypt_t *crypt)
{
if (!crypt) {
- return 0;
+ return 0; // LCOV_EXCL_LINE
}
if (crypt->cfb.obj) {
EVP_CIPHER_CTX_free(crypt->cfb.obj);
@@ -149,7 +153,7 @@ pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
uint64_t buf64[512]; // 4KB - page size
uint64_t iv64[2];
size_t blocks, blockb;
- unsigned blsize = crypt->blocksize;
+ size_t blsize = crypt->blocksize;
/* encrypting till the block boundary */
while (bytes && crypt->cfb.remaining) {
@@ -182,7 +186,7 @@ pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
EVP_EncryptUpdate(
crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 16);
if (outlen != 16) {
- RNP_LOG("Bad outlen: must be 16");
+ RNP_LOG("Bad outlen: must be 16"); // LCOV_EXCL_LINE
}
*in64 ^= iv64[0];
iv64[0] = *in64++;
@@ -196,7 +200,7 @@ pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
EVP_EncryptUpdate(
crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 8);
if (outlen != 8) {
- RNP_LOG("Bad outlen: must be 8");
+ RNP_LOG("Bad outlen: must be 8"); // LCOV_EXCL_LINE
}
*in64 ^= iv64[0];
iv64[0] = *in64++;
@@ -218,7 +222,7 @@ pgp_cipher_cfb_encrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
int outlen = blsize;
EVP_EncryptUpdate(crypt->cfb.obj, crypt->cfb.iv, &outlen, crypt->cfb.iv, (int) blsize);
if (outlen != (int) blsize) {
- RNP_LOG("Bad outlen: must be %u", blsize);
+ RNP_LOG("Bad outlen: must be %zu", blsize); // LCOV_EXCL_LINE
}
crypt->cfb.remaining = blsize;
@@ -243,7 +247,7 @@ pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
uint64_t outbuf64[512];
uint64_t iv64[2];
size_t blocks, blockb;
- unsigned blsize = crypt->blocksize;
+ size_t blsize = crypt->blocksize;
/* decrypting till the block boundary */
while (bytes && crypt->cfb.remaining) {
@@ -279,7 +283,7 @@ pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
EVP_EncryptUpdate(
crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 16);
if (outlen != 16) {
- RNP_LOG("Bad outlen: must be 16");
+ RNP_LOG("Bad outlen: must be 16"); // LCOV_EXCL_LINE
}
*out64++ = *in64 ^ iv64[0];
iv64[0] = *in64++;
@@ -293,7 +297,7 @@ pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
EVP_EncryptUpdate(
crypt->cfb.obj, (uint8_t *) iv64, &outlen, (uint8_t *) iv64, 8);
if (outlen != 8) {
- RNP_LOG("Bad outlen: must be 8");
+ RNP_LOG("Bad outlen: must be 8"); // LCOV_EXCL_LINE
}
*out64++ = *in64 ^ iv64[0];
iv64[0] = *in64++;
@@ -315,7 +319,7 @@ pgp_cipher_cfb_decrypt(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
int outlen = blsize;
EVP_EncryptUpdate(crypt->cfb.obj, crypt->cfb.iv, &outlen, crypt->cfb.iv, (int) blsize);
if (outlen != (int) blsize) {
- RNP_LOG("Bad outlen: must be %u", blsize);
+ RNP_LOG("Bad outlen: must be %zu", blsize); // LCOV_EXCL_LINE
}
crypt->cfb.remaining = blsize;
@@ -435,14 +439,18 @@ pgp_cipher_aead_init(pgp_crypt_t * crypt,
}
auto cipher = EVP_get_cipherbyname(algname);
if (!cipher) {
+ /* LCOV_EXCL_START */
RNP_LOG("Cipher %s is not supported.", algname);
return false;
+ /* LCOV_EXCL_END */
}
/* Create and setup context */
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to create cipher context: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
crypt->aead.key = new rnp::secure_vector<uint8_t>(key, key + pgp_key_size(ealg));
@@ -510,21 +518,29 @@ pgp_cipher_aead_start(pgp_crypt_t *crypt, const uint8_t *nonce, size_t len)
assert(len == aead.n_len);
EVP_CIPHER_CTX_reset(ctx);
if (EVP_CipherInit_ex(ctx, aead.cipher, NULL, NULL, NULL, enc) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to initialize cipher: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, aead.n_len, NULL) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set nonce length: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
if (EVP_CipherInit_ex(ctx, NULL, NULL, aead.key->data(), nonce, enc) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to start cipher: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
int adlen = 0;
if (EVP_CipherUpdate(ctx, NULL, &adlen, aead.ad, aead.ad_len) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set AD: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
return true;
}
@@ -538,7 +554,7 @@ pgp_cipher_aead_update(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
int out_len = 0;
bool res = EVP_CipherUpdate(crypt->aead.obj, out, &out_len, in, len) == 1;
if (!res) {
- RNP_LOG("Failed to update cipher: %lu", ERR_peek_last_error());
+ RNP_LOG("Failed to update cipher: %lu", ERR_peek_last_error()); // LCOV_EXCL_LINE
}
assert(out_len == (int) len);
return res;
@@ -558,26 +574,34 @@ pgp_cipher_aead_finish(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
if (aead.decrypt) {
assert(len >= aead.taglen);
if (len < aead.taglen) {
+ /* LCOV_EXCL_START */
RNP_LOG("Invalid state: too few input bytes.");
return false;
+ /* LCOV_EXCL_END */
}
size_t data_len = len - aead.taglen;
int out_len = 0;
if (EVP_CipherUpdate(ctx, out, &out_len, in, data_len) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to update cipher: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
uint8_t tag[PGP_AEAD_MAX_TAG_LEN] = {0};
memcpy(tag, in + data_len, aead.taglen);
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, aead.taglen, tag) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to set tag: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
int out_len2 = 0;
if (EVP_CipherFinal_ex(ctx, out + out_len, &out_len2) != 1) {
/* Zero value if auth tag is incorrect */
if (ERR_peek_last_error()) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to finish AEAD decryption: %lu", ERR_peek_last_error());
+ /* LCOV_EXCL_END */
}
return false;
}
@@ -585,18 +609,24 @@ pgp_cipher_aead_finish(pgp_crypt_t *crypt, uint8_t *out, const uint8_t *in, size
} else {
int out_len = 0;
if (EVP_CipherUpdate(ctx, out, &out_len, in, len) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to update cipher: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
int out_len2 = 0;
if (EVP_CipherFinal_ex(ctx, out + out_len, &out_len2) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to finish AEAD encryption: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
assert(out_len + out_len2 == (int) len);
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, aead.taglen, out + len) != 1) {
+ /* LCOV_EXCL_START */
RNP_LOG("Failed to get tag: %lu", ERR_peek_last_error());
return false;
+ /* LCOV_EXCL_END */
}
}
return true;
diff --git a/src/lib/rnp.cpp b/src/lib/rnp.cpp
index 24c46f9..9e92aaf 100644
--- a/src/lib/rnp.cpp
+++ b/src/lib/rnp.cpp
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2017-2021, Ribose Inc.
+ * Copyright (c) 2017-2023, Ribose Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -6197,6 +6197,20 @@ try {
FFI_GUARD
rnp_result_t
+rnp_signature_get_features(rnp_signature_handle_t handle, uint32_t *features)
+try {
+ if (!handle || !features) {
+ return RNP_ERROR_NULL_POINTER;
+ }
+ if (!handle->sig) {
+ return RNP_ERROR_BAD_PARAMETERS;
+ }
+ *features = handle->sig->sig.key_get_features();
+ return RNP_SUCCESS;
+}
+FFI_GUARD
+
+rnp_result_t
rnp_signature_get_keyid(rnp_signature_handle_t handle, char **result)
try {
if (!handle || !result) {
@@ -7750,7 +7764,10 @@ key_to_json(json_object *jso, rnp_key_handle_t handle, uint32_t flags)
}
json_object_object_add(jso, "key wrap cipher", jsocipher);
}
+
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
case PGP_PKA_ECDSA:
case PGP_PKA_EDDSA:
case PGP_PKA_SM2: {
@@ -8313,7 +8330,9 @@ try {
}
pgp_armored_msg_t msgtype = PGP_ARMORED_UNKNOWN;
- if (is_armored_source(&input->src)) {
+ if (is_cleartext_source(&input->src)) {
+ msgtype = PGP_ARMORED_CLEARTEXT;
+ } else if (is_armored_source(&input->src)) {
msgtype = rnp_armored_get_type(&input->src);
} else {
msgtype = rnp_armor_guess_type(&input->src);
diff --git a/src/librekey/g23_sexp.hpp b/src/librekey/g23_sexp.hpp
index b888680..b062c52 100644
--- a/src/librekey/g23_sexp.hpp
+++ b/src/librekey/g23_sexp.hpp
@@ -27,8 +27,8 @@
#ifndef RNP_G23_SEXP_HPP
#define RNP_G23_SEXP_HPP
-#include "sexp/sexp.h"
-#include "sexp/ext-key-format.h"
+#include "sexpp/sexp.h"
+#include "sexpp/ext-key-format.h"
#define SXP_MAX_DEPTH 30
diff --git a/src/librepgp/stream-armor.cpp b/src/librepgp/stream-armor.cpp
index 669c305..b6669dc 100644
--- a/src/librepgp/stream-armor.cpp
+++ b/src/librepgp/stream-armor.cpp
@@ -235,7 +235,8 @@ armor_read_trailer(pgp_source_t *src)
size_t stlen;
pgp_source_armored_param_t *param = (pgp_source_armored_param_t *) src->param;
- if (!armor_skip_chars(param->readsrc, "\r\n")) {
+ /* Space or tab could get between armor and trailer, see issue #2199 */
+ if (!armor_skip_chars(param->readsrc, "\r\n \t")) {
return false;
}
@@ -1159,6 +1160,9 @@ is_armored_source(pgp_source_t *src)
return false;
}
buf[read - 1] = 0;
+ if (!!strstr((char *) buf, ST_CLEAR_BEGIN)) {
+ return false;
+ }
return !!strstr((char *) buf, ST_ARMOR_BEGIN);
}
diff --git a/src/librepgp/stream-parse.cpp b/src/librepgp/stream-parse.cpp
index 5ec4d64..f69f222 100644
--- a/src/librepgp/stream-parse.cpp
+++ b/src/librepgp/stream-parse.cpp
@@ -1727,11 +1727,11 @@ init_literal_src(pgp_source_t *src, pgp_source_t *readsrc)
case 'u':
case 'l':
case '1':
+ case 'm':
break;
default:
- RNP_LOG("unknown data format %" PRIu8, format);
- ret = RNP_ERROR_BAD_FORMAT;
- goto finish;
+ RNP_LOG("Warning: unknown data format %" PRIu8 ", ignoring.", format);
+ break;
}
param->hdr.format = format;
/* file name */
diff --git a/src/librepgp/stream-sig.cpp b/src/librepgp/stream-sig.cpp
index 6f3bc81..ab4098f 100644
--- a/src/librepgp/stream-sig.cpp
+++ b/src/librepgp/stream-sig.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2022, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2018-2023, [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -1005,6 +1005,13 @@ pgp_signature_t::set_revocation_reason(pgp_revocation_type_t code, const std::st
}
}
+pgp_key_feature_t
+pgp_signature_t::key_get_features() const
+{
+ const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_FEATURES);
+ return (pgp_key_feature_t)(subpkt ? subpkt->data[0] : 0);
+}
+
bool
pgp_signature_t::key_has_features(pgp_key_feature_t flags) const
{
@@ -1393,7 +1400,9 @@ pgp_signature_t::parse_material(pgp_signature_material_t &material) const
if (version < PGP_V4) {
RNP_LOG("Warning! v3 EdDSA signature.");
}
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
case PGP_PKA_ECDSA:
case PGP_PKA_SM2:
case PGP_PKA_ECDH:
diff --git a/src/librepgp/stream-sig.h b/src/librepgp/stream-sig.h
index 4f36c38..943efa9 100644
--- a/src/librepgp/stream-sig.h
+++ b/src/librepgp/stream-sig.h
@@ -274,6 +274,8 @@ typedef struct pgp_signature_t {
*/
void set_revocation_reason(pgp_revocation_type_t code, const std::string &reason);
+ pgp_key_feature_t key_get_features() const;
+
/**
* @brief Check whether signer's key supports certain feature(s). Makes sense only for
* self-signature, for more details see the RFC 4880bis, 5.2.3.25. If there is
diff --git a/src/rnp/fficli.cpp b/src/rnp/fficli.cpp
index fa118ee..b6e1b9a 100644
--- a/src/rnp/fficli.cpp
+++ b/src/rnp/fficli.cpp
@@ -2946,7 +2946,6 @@ cli_rnp_print_signatures(cli_rnp_t *rnp, const std::vector<rnp_op_verify_signatu
{
unsigned invalidc = 0;
unsigned unknownc = 0;
- unsigned validc = 0;
std::string title = "UNKNOWN signature";
FILE * resfp = rnp->resfp;
@@ -2955,7 +2954,6 @@ cli_rnp_print_signatures(cli_rnp_t *rnp, const std::vector<rnp_op_verify_signatu
switch (status) {
case RNP_SUCCESS:
title = "Good signature";
- validc++;
break;
case RNP_ERROR_SIGNATURE_EXPIRED:
title = "EXPIRED signature";
diff --git a/src/rnp/rnp.cpp b/src/rnp/rnp.cpp
index 30d3ac4..5dddaca 100644
--- a/src/rnp/rnp.cpp
+++ b/src/rnp/rnp.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2021 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023 [Ribose Inc](https://www.ribose.com).
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
@@ -297,7 +297,9 @@ setcmd(rnp_cfg &cfg, int cmd, const char *arg)
break;
case CMD_CLEARSIGN:
cfg.set_bool(CFG_CLEARTEXT, true);
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
case CMD_SIGN:
cfg.set_bool(CFG_NEEDSSECKEY, true);
cfg.set_bool(CFG_SIGN_NEEDED, true);
@@ -314,7 +316,9 @@ setcmd(rnp_cfg &cfg, int cmd, const char *arg)
case CMD_VERIFY:
/* single verify will discard output, decrypt will not */
cfg.set_bool(CFG_NO_OUTPUT, true);
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
case CMD_VERIFY_CAT:
newcmd = CMD_PROCESS;
break;
@@ -588,7 +592,9 @@ set_short_option(rnp_cfg &cfg, int ch, const char *arg)
cfg.set_bool(CFG_KEYSTORE_DISABLED, true);
break;
case 'h':
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
default:
return setcmd(cfg, CMD_HELP, optarg);
}
diff --git a/src/rnpkeys/main.cpp b/src/rnpkeys/main.cpp
index 8bcb7e1..3dd088c 100644
--- a/src/rnpkeys/main.cpp
+++ b/src/rnpkeys/main.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023, [Ribose Inc](https://www.ribose.com).
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
@@ -41,7 +41,7 @@
extern struct option options[];
extern const char * usage;
-optdefs_t
+static optdefs_t
get_short_cmd(int ch)
{
switch (ch) {
@@ -52,7 +52,9 @@ get_short_cmd(int ch)
case 'l':
return CMD_LIST_KEYS;
case 'h':
+#if (!defined(_MSVC_LANG) || _MSVC_LANG >= 201703L)
[[fallthrough]];
+#endif
default:
return CMD_HELP;
}
diff --git a/src/rnpkeys/rnpkeys.cpp b/src/rnpkeys/rnpkeys.cpp
index 1a6997c..6a7885d 100644
--- a/src/rnpkeys/rnpkeys.cpp
+++ b/src/rnpkeys/rnpkeys.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2021, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023, [Ribose Inc](https://www.ribose.com).
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
@@ -260,8 +260,8 @@ import_keys(cli_rnp_t *rnp, rnp_input_t input, const std::string &inname)
} while (1);
// print statistics
- ERR_MSG("Import finished: %lu key%s processed, %lu new public keys, %lu new secret keys, "
- "%lu updated, %lu unchanged.",
+ ERR_MSG("Import finished: %zu key%s processed, %zu new public keys, %zu new secret keys, "
+ "%zu updated, %zu unchanged.",
processed_keys,
(processed_keys != 1) ? "s" : "",
new_pub_keys.size(),
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 7d2a6b0..d1a89d4 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -66,13 +66,26 @@ else()
endif()
find_package(JSON-C 0.11 REQUIRED)
-if (CRYPTO_BACKEND_LOWERCASE STREQUAL "botan")
- find_package(Botan2 2.14.0 REQUIRED)
+if (CRYPTO_BACKEND_BOTAN3)
+ find_package(Botan 3.0.0 REQUIRED)
+elseif (CRYPTO_BACKEND_BOTAN)
+ find_package(Botan 2.14.0 REQUIRED)
+ if(BOTAN_VERSION VERSION_GREATER_EQUAL 3.0.0)
+ set(CRYPTO_BACKEND_BOTAN3 1)
+ endif()
endif()
if (CRYPTO_BACKEND_LOWERCASE STREQUAL "openssl")
find_package(OpenSSL 1.1.1 REQUIRED)
endif()
+if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.8.5")
+ set(CMAKE_CXX_STANDARD 14)
+endif()
+
+if(CRYPTO_BACKEND_BOTAN3)
+ set(CMAKE_CXX_STANDARD 20)
+endif()
+
add_executable(rnp_tests
../rnp/rnpcfg.cpp
../rnp/fficli.cpp
@@ -170,13 +183,14 @@ target_include_directories(rnp_tests
PRIVATE
"${PROJECT_SOURCE_DIR}/src"
"${PROJECT_SOURCE_DIR}/src/lib"
- "${BOTAN2_INCLUDE_DIRS}"
+ "${BOTAN_INCLUDE_DIRS}"
+ "${SEXPP_INCLUDE_DIRS}"
)
target_link_libraries(rnp_tests
PRIVATE
librnp-static
JSON-C::JSON-C
- sexp
+ sexpp
${GTestMain}
)
if (CRYPTO_BACKEND_LOWERCASE STREQUAL "openssl")
@@ -220,6 +234,13 @@ function(add_cli_test suite)
"RNP_TESTS_GPG_PATH=${GPG_EXECUTABLE}"
"RNP_TESTS_GPGCONF_PATH=${GPGCONF_EXECUTABLE}"
)
+ if (CRYPTO_BACKEND_OPENSSL)
+ get_filename_component(ossl_root "${OPENSSL_INCLUDE_DIR}" DIRECTORY)
+ list(APPEND _env
+ "RNP_TESTS_OPENSSL_ROOT=${ossl_root}"
+ )
+ endif()
+
set_tests_properties(${_test_name} PROPERTIES
TIMEOUT 3000
FIXTURES_REQUIRED testdata
diff --git a/src/tests/cipher.cpp b/src/tests/cipher.cpp
index 25b98bf..3df5f0b 100644
--- a/src/tests/cipher.cpp
+++ b/src/tests/cipher.cpp
@@ -216,17 +216,15 @@ TEST_F(rnp_tests, rnp_test_x25519)
}
static void
-elgamal_roundtrip(pgp_eg_key_t *key)
+elgamal_roundtrip(pgp_eg_key_t *key, rnp::RNG &rng)
{
const uint8_t in_b[] = {0x01, 0x02, 0x03, 0x04, 0x17};
pgp_eg_encrypted_t enc = {{{0}}};
uint8_t res[1024];
size_t res_len = 0;
- assert_int_equal(elgamal_encrypt_pkcs1(&global_ctx.rng, &enc, in_b, sizeof(in_b), key),
- RNP_SUCCESS);
- assert_int_equal(elgamal_decrypt_pkcs1(&global_ctx.rng, res, &res_len, &enc, key),
- RNP_SUCCESS);
+ assert_int_equal(elgamal_encrypt_pkcs1(&rng, &enc, in_b, sizeof(in_b), key), RNP_SUCCESS);
+ assert_int_equal(elgamal_decrypt_pkcs1(&rng, res, &res_len, &enc, key), RNP_SUCCESS);
assert_int_equal(res_len, sizeof(in_b));
assert_true(bin_eq_hex(res, res_len, "0102030417"));
}
@@ -236,7 +234,7 @@ TEST_F(rnp_tests, raw_elgamal_random_key_test_success)
pgp_eg_key_t key;
assert_int_equal(elgamal_generate(&global_ctx.rng, &key, 1024), RNP_SUCCESS);
- elgamal_roundtrip(&key);
+ elgamal_roundtrip(&key, global_ctx.rng);
}
TEST_F(rnp_tests, ecdsa_signverify_success)
@@ -1011,3 +1009,89 @@ TEST_F(rnp_tests, test_brainpool_enabled)
assert_false(supported);
#endif
}
+
+#if defined(CRYPTO_BACKEND_BOTAN)
+TEST_F(rnp_tests, test_windows_botan_crash)
+{
+ /* Reproducer for https://github.com/randombit/botan/issues/3812 . Related CLI test
+ * test_sym_encrypted__rnp_aead_botan_crash */
+
+ auto data = file_to_vec("data/test_messages/message.aead-windows-issue-botan");
+ /* First 32 bytes are encrypted key as it was extracted from the OpenPGP stream, so
+ * skipping. */
+ uint8_t *idx = data.data() + 32;
+ uint8_t bufbin[64] = {0};
+ uint8_t outbuf[32768] = {0};
+ size_t outsz = sizeof(outbuf);
+ size_t written = 0;
+ size_t read = 0;
+ size_t diff = 0;
+
+ /* Now the data which exposes a possible crash */
+ struct botan_cipher_struct *cipher = NULL;
+ assert_int_equal(botan_cipher_init(&cipher, "AES-128/OCB", BOTAN_CIPHER_INIT_FLAG_DECRYPT),
+ 0);
+
+ const char *key2 = "417835a476bc5958b18d41fb00cf682d";
+ assert_int_equal(rnp::hex_decode(key2, bufbin, 16), 16);
+ assert_int_equal(botan_cipher_set_key(cipher, bufbin, 16), 0);
+
+ const char *ad2 = "d40107020c0000000000000000";
+ assert_int_equal(rnp::hex_decode(ad2, bufbin, 13), 13);
+ assert_int_equal(botan_cipher_set_associated_data(cipher, bufbin, 13), 0);
+
+ const char *nonce2 = "005dbbbe0088f9d17ca2d8d464920f";
+ assert_int_equal(rnp::hex_decode(nonce2, bufbin, 15), 15);
+ assert_int_equal(botan_cipher_start(cipher, bufbin, 15), 0);
+
+ assert_int_equal(
+ botan_cipher_update(cipher, 0, outbuf, outsz, &written, idx, 32736, &read), 0);
+ diff = 32736 - read;
+ idx += read;
+
+ assert_int_equal(
+ botan_cipher_update(cipher, 0, outbuf, outsz, &written, idx, diff + 32736, &read), 0);
+ idx += read;
+ diff = diff + 32736 - read;
+
+ assert_int_equal(
+ botan_cipher_update(cipher, 0, outbuf, outsz, &written, idx, diff + 32736, &read), 0);
+ idx += read;
+ diff = diff + 32736 - read;
+
+ assert_int_equal(
+ botan_cipher_update(cipher, 0, outbuf, outsz, &written, idx, diff + 32736, &read), 0);
+ idx += read;
+ diff = diff + 32736 - read;
+
+ uint32_t ver_major = botan_version_major();
+ uint32_t ver_minor = botan_version_minor();
+ uint32_t ver_patch = botan_version_patch();
+ uint32_t ver = (ver_major << 16) | (ver_minor << 8) | ver_patch;
+ uint32_t ver_2_19_3 = (2 << 16) | (19 << 8) | 3;
+ uint32_t ver_3_2_0 = (3 << 16) | (2 << 8);
+ bool check = true;
+ /* Currently AV happens with versions up to 2.19.3 and 3.2.0 */
+ if ((ver_major == 2) && (ver <= ver_2_19_3)) {
+ check = false;
+ }
+ if ((ver_major == 3) && (ver <= ver_3_2_0)) {
+ check = false;
+ }
+
+ if (check) {
+ assert_int_equal(botan_cipher_update(cipher,
+ BOTAN_CIPHER_UPDATE_FLAG_FINAL,
+ outbuf,
+ outsz,
+ &written,
+ idx,
+ diff + 25119,
+ &read),
+ 0);
+ }
+
+ assert_int_equal(botan_cipher_reset(cipher), 0);
+ assert_int_equal(botan_cipher_destroy(cipher), 0);
+}
+#endif
diff --git a/src/tests/cipher_cxx.cpp b/src/tests/cipher_cxx.cpp
index b5f7f83..a33e38e 100644
--- a/src/tests/cipher_cxx.cpp
+++ b/src/tests/cipher_cxx.cpp
@@ -138,7 +138,7 @@ test_cipher(pgp_symm_alg_t alg,
std::vector<uint8_t> decrypted(ct.size());
// all except the last block but see below for openssl
nonfinal_bytes = rnp_round_up(ct.size(), ud) - ud;
-#ifdef CRYPTO_BACKEND_OPENSSL
+#if defined(CRYPTO_BACKEND_OPENSSL) || defined(CRYPTO_BACKEND_BOTAN3)
/* Since ossl backend sets tag explicitly tag bytes cannot be
split between two blocks.
The issue may easily occur is (for example)
@@ -146,6 +146,7 @@ test_cipher(pgp_symm_alg_t alg,
ct.size() = 24
tag_size=16
*/
+ /* Botan 3 also requires to include whole tag in the finish() call. */
if (ct.size() - nonfinal_bytes < tag_size) {
nonfinal_bytes = ct.size() - tag_size;
}
@@ -153,12 +154,16 @@ test_cipher(pgp_symm_alg_t alg,
output_written = 0;
input_consumed = 0;
while (input_consumed != nonfinal_bytes) {
+ size_t consume = std::min(ud, nonfinal_bytes - input_consumed);
+ if (consume < ud) {
+ break;
+ }
assert_true(dec->update(decrypted.data() + output_written,
decrypted.size() - output_written,
&written,
(const uint8_t *) ct.data() + input_consumed,
// ++++ ud,
- std::min(ud, nonfinal_bytes - input_consumed),
+ consume,
&consumed));
output_written += written;
input_consumed += consumed;
diff --git a/src/tests/cli_common.py b/src/tests/cli_common.py
index 12bf5d8..f8d7001 100644
--- a/src/tests/cli_common.py
+++ b/src/tests/cli_common.py
@@ -1,10 +1,10 @@
import sys
-import distutils.spawn
import random
import string
import logging
import os
import re
+import shutil
from subprocess import Popen, PIPE
RNP_ROOT = None
@@ -78,7 +78,7 @@ def file_text(path, encoding = CONSOLE_ENCODING):
return f.read().decode(encoding).replace('\r\r', '\r')
def find_utility(name, exitifnone = True):
- path = distutils.spawn.find_executable(name)
+ path = shutil.which(name)
if not path and exitifnone:
logging.error('Cannot find utility {}. Exiting.'.format(name))
sys.exit(1)
diff --git a/src/tests/cli_tests.py b/src/tests/cli_tests.py
index e6f5ed7..47f6890 100755
--- a/src/tests/cli_tests.py
+++ b/src/tests/cli_tests.py
@@ -9,6 +9,7 @@ import sys
import tempfile
import time
import unittest
+import random
from platform import architecture
from cli_common import (file_text, find_utility, is_windows, list_upto,
@@ -47,6 +48,8 @@ RNP_IDEA = True
RNP_BLOWFISH = True
RNP_CAST5 = True
RNP_RIPEMD160 = True
+# Botan may cause AV during OCB decryption in certain cases, see https://github.com/randombit/botan/issues/3812
+RNP_BOTAN_OCB_AV = False
if sys.version_info >= (3,):
unichr = chr
@@ -861,6 +864,7 @@ def gpg_check_features():
def rnp_check_features():
global RNP_TWOFISH, RNP_BRAINPOOL, RNP_AEAD, RNP_AEAD_EAX, RNP_AEAD_OCB, RNP_AEAD_OCB_AES, RNP_IDEA, RNP_BLOWFISH, RNP_CAST5, RNP_RIPEMD160
+ global RNP_BOTAN_OCB_AV
ret, out, _ = run_proc(RNP, ['--version'])
if ret != 0:
raise_err('Failed to get RNP version.')
@@ -869,6 +873,14 @@ def rnp_check_features():
RNP_AEAD_OCB = re.match(r'(?s)^.*AEAD:.*OCB.*', out) is not None
RNP_AEAD = RNP_AEAD_EAX or RNP_AEAD_OCB
RNP_AEAD_OCB_AES = RNP_AEAD_OCB and re.match(r'(?s)^.*Backend.*OpenSSL.*', out) is not None
+ # Botan OCB crash
+ if re.match(r'(?s)^.*Backend.*Botan.*', out):
+ match = re.match(r'(?s)^.*Backend version: ([\d]+)\.([\d]+)\.([\d]+).*$', out)
+ ver = [int(match.group(1)), int(match.group(2)), int(match.group(3))]
+ if ver <= [2, 19, 3]:
+ RNP_BOTAN_OCB_AV = True
+ if (ver >= [3, 0, 0]) and (ver <= [3, 2, 0]):
+ RNP_BOTAN_OCB_AV = True
# Twofish
RNP_TWOFISH = re.match(r'(?s)^.*Encryption:.*TWOFISH.*', out) is not None
# Brainpool curves
@@ -887,6 +899,7 @@ def rnp_check_features():
print('RNP_AEAD_EAX: ' + str(RNP_AEAD_EAX))
print('RNP_AEAD_OCB: ' + str(RNP_AEAD_OCB))
print('RNP_AEAD_OCB_AES: ' + str(RNP_AEAD_OCB_AES))
+ print('RNP_BOTAN_OCB_AV: ' + str(RNP_BOTAN_OCB_AV))
def setup(loglvl):
# Setting up directories.
@@ -3037,10 +3050,10 @@ class Misc(unittest.TestCase):
def test_backend_version(self):
BOTAN_BACKEND_VERSION = r'(?s)^.*.' \
'Backend: Botan.*' \
- 'Backend version: ([a-zA-z\.0-9]+).*$'
+ 'Backend version: ([a-zA-Z\\.0-9]+).*$'
OPENSSL_BACKEND_VERSION = r'(?s)^.*' \
'Backend: OpenSSL.*' \
- 'Backend version: ([a-zA-z\.0-9]+).*$'
+ 'Backend version: ([a-zA-Z\\.0-9]+).*$'
# Run without parameters and make sure it matches
ret, out, _ = run_proc(RNP, [])
self.assertNotEqual(ret, 0)
@@ -3054,28 +3067,33 @@ class Misc(unittest.TestCase):
if not match:
match = re.match(OPENSSL_BACKEND_VERSION, out)
backend_prog = 'openssl'
- openssl_root = os.getenv('OPENSSL_ROOT_DIR')
+ openssl_root = os.getenv('RNP_TESTS_OPENSSL_ROOT')
else:
openssl_root = None
self.assertTrue(match)
# check there is no unexpected output
self.assertNotRegex(err, r'(?is)^.*Unsupported.*$')
self.assertNotRegex(err, r'(?is)^.*pgp_sa_to_openssl_string.*$')
+ self.assertNotRegex(err, r'(?is)^.*unknown.*$')
# In case when there are several openssl installations
# testing environment is supposed to point to the right one
# through OPENSSL_ROOT_DIR environment variable
+ if is_windows():
+ backend_prog += '.exe'
+ backend_prog_ext = None
if openssl_root is not None:
- backen_prog_ext = shutil.which(backend_prog, path = openssl_root + '/bin')
+ backend_prog_ext = shutil.which(backend_prog, path = openssl_root + '/bin')
else:
# In all other cases
# check that botan or openssl executable binary exists in PATH
- backen_prog_ext = shutil.which(backend_prog)
+ backend_prog_ext = shutil.which(backend_prog)
- if backen_prog_ext is not None:
- ret, out, _ = run_proc(backen_prog_ext, ['version'])
- self.assertEqual(ret, 0)
- self.assertIn(match.group(1), out)
+ if backend_prog_ext is None:
+ return
+ ret, out, _ = run_proc(backend_prog_ext, ['version'])
+ self.assertEqual(ret, 0)
+ self.assertIn(match.group(1), out)
def test_help_message(self):
# rnp help message
@@ -3971,6 +3989,17 @@ class Misc(unittest.TestCase):
clear_workfiles()
shutil.rmtree(RNP2, ignore_errors=True)
+ def test_armored_detection_on_cleartext(self):
+ ret, out, err = run_proc(RNP, ['--keyfile', data_path(SECRING_1), '--password', PASSWORD, '--clearsign'], 'Hello\n')
+ self.assertEqual(ret, 0)
+ self.assertRegex(out, r'(?s)^.*BEGIN PGP SIGNED MESSAGE.*$')
+ self.assertRegex(out, r'(?s)^.*BEGIN PGP SIGNATURE.*$')
+ ret, _, err = run_proc(RNP, ['--keyfile', data_path(PUBRING_1), '--verify', '-'], out)
+ self.assertEqual(ret, 0)
+ self.assertRegex(err, r'(?s)^.*Good signature made.*$')
+ self.assertNotRegex(err, r'(?s)^.*Warning: missing or malformed CRC line.*$')
+ self.assertNotRegex(err, r'(?s)^.*wrong armor trailer.*$')
+
class Encryption(unittest.TestCase):
'''
Things to try later:
@@ -4120,12 +4149,25 @@ class Encryption(unittest.TestCase):
AEAD_C = list_upto(CIPHERS, Encryption.RUNS)
AEAD_M = list_upto(AEADS, Encryption.RUNS)
AEAD_B = list_upto([None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16], Encryption.RUNS)
+ SIZES = Encryption.SIZES_R
+ random.shuffle(SIZES)
# Encrypt and decrypt cleartext using the AEAD
- for size, cipher, aead, bits, z in zip(Encryption.SIZES_R, AEAD_C,
+ for size, cipher, aead, bits, z in zip(SIZES, AEAD_C,
AEAD_M, AEAD_B, Encryption.Z_R):
+ if RNP_BOTAN_OCB_AV and (aead == 'ocb') and (size > 30000):
+ continue
rnp_sym_encryption_rnp_aead(size, cipher, z, [aead, bits], GPG_AEAD)
+ def test_sym_encrypted__rnp_aead_botan_crash(self):
+ if RNP_BOTAN_OCB_AV:
+ return
+ dst, = reg_workfiles('cleartext', '.txt')
+ rnp_decrypt_file(data_path('test_messages/message.aead-windows-issue'), dst)
+ remove_files(dst)
+ rnp_decrypt_file(data_path('test_messages/message.aead-windows-issue2'), dst)
+ remove_files(dst)
+
def test_aead_chunk_edge_cases(self):
if not RNP_AEAD:
print('AEAD is not available for RNP - skipping.')
@@ -4710,12 +4752,16 @@ class EncryptElgamal(Encrypt):
self.operation_key_location = tuple((key_path(pfx, False), key_path(pfx, True)))
self.rnp.userid = self.gpg.userid = pfx + AT_EXAMPLE
# DSA 1024 key uses SHA-1 as hash but verification would succeed till 2024
+ if sign_key_size == 1024:
+ return
self._encrypt_decrypt(self.gpg, self.rnp)
def do_test_decrypt(self, sign_key_size, enc_key_size):
pfx = EncryptElgamal.key_pfx(sign_key_size, enc_key_size)
self.operation_key_location = tuple((key_path(pfx, False), key_path(pfx, True)))
self.rnp.userid = self.gpg.userid = pfx + AT_EXAMPLE
+ if sign_key_size == 1024:
+ return
self._encrypt_decrypt(self.rnp, self.gpg)
def test_encrypt_P1024_1024(self): self.do_test_encrypt(1024, 1024)
@@ -4726,11 +4772,7 @@ class EncryptElgamal(Encrypt):
def test_decrypt_P2048_2048(self): self.do_test_decrypt(2048, 2048)
def test_decrypt_P1234_1234(self): self.do_test_decrypt(1234, 1234)
- def test_generate_elgamal_key1024_in_gpg_and_encrypt(self):
- cmd = EncryptElgamal.GPG_GENERATE_DSA_ELGAMAL_PATTERN.format(1024, 1024, self.gpg.userid)
- self.operation_key_gencmd = cmd
- # Will not fail till 2024 since 1024-bit DSA key uses SHA-1 as hash.
- self._encrypt_decrypt(self.gpg, self.rnp)
+ # 1024-bit key generation test was removed since it uses SHA1, which is not allowed for key signatures since Jan 19, 2024.
def test_generate_elgamal_key1536_in_gpg_and_encrypt(self):
cmd = EncryptElgamal.GPG_GENERATE_DSA_ELGAMAL_PATTERN.format(1536, 1536, self.gpg.userid)
diff --git a/src/tests/data/test_messages/message.aead-windows-issue b/src/tests/data/test_messages/message.aead-windows-issue
new file mode 100644
index 0000000..4836610
--- /dev/null
+++ b/src/tests/data/test_messages/message.aead-windows-issue
Binary files differ
diff --git a/src/tests/data/test_messages/message.aead-windows-issue-botan b/src/tests/data/test_messages/message.aead-windows-issue-botan
new file mode 100644
index 0000000..84e17e8
--- /dev/null
+++ b/src/tests/data/test_messages/message.aead-windows-issue-botan
Binary files differ
diff --git a/src/tests/data/test_messages/message.aead-windows-issue2 b/src/tests/data/test_messages/message.aead-windows-issue2
new file mode 100644
index 0000000..9af3d50
--- /dev/null
+++ b/src/tests/data/test_messages/message.aead-windows-issue2
Binary files differ
diff --git a/src/tests/data/test_messages/message.txt.signed-mimemode b/src/tests/data/test_messages/message.txt.signed-mimemode
new file mode 100644
index 0000000..27f7579
--- /dev/null
+++ b/src/tests/data/test_messages/message.txt.signed-mimemode
Binary files differ
diff --git a/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line-2.asc b/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line-2.asc
new file mode 100644
index 0000000..dbec69f
--- /dev/null
+++ b/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line-2.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEWsN6MBYJKwYBBAHaRw8BAQdAAS+nkv9BdVi0JX7g6d+O201bdKhdowbielOo
+ugCpCfi0CWVjYy0yNTUxOYiUBBMWCAA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgID
+AQIeAwIXgBYhBCH8aCdKrjtd45pCd8x4YniYGwcoBQJcVa/NAAoJEMx4YniYGwco
+lFAA/jMt3RUUb5xt63JW6HFcrYq0RrDAcYMsXAY73iZpPsEcAQDmKbH21LkwoClU
+9RrUJSYZnMla/pQdgOxd7/PjRCpbCg==
+=miZp
+
+
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line.asc b/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line.asc
new file mode 100644
index 0000000..9617fb9
--- /dev/null
+++ b/src/tests/data/test_stream_key_load/ecc-25519-pub-extra-line.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEWsN6MBYJKwYBBAHaRw8BAQdAAS+nkv9BdVi0JX7g6d+O201bdKhdowbielOo
+ugCpCfi0CWVjYy0yNTUxOYiUBBMWCAA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgID
+AQIeAwIXgBYhBCH8aCdKrjtd45pCd8x4YniYGwcoBQJcVa/NAAoJEMx4YniYGwco
+lFAA/jMt3RUUb5xt63JW6HFcrYq0RrDAcYMsXAY73iZpPsEcAQDmKbH21LkwoClU
+9RrUJSYZnMla/pQdgOxd7/PjRCpbCg==
+=miZp
+
+
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp
index 55b0d10..40b17cc 100644
--- a/src/tests/ffi-enc.cpp
+++ b/src/tests/ffi-enc.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2020-2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -178,15 +178,16 @@ TEST_F(rnp_tests, test_ffi_encrypt_pass)
assert_rnp_failure(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH"));
assert_rnp_failure(
rnp_op_encrypt_add_password(op, "pass2", "SHA256", 12345, "TWOFISH"));
- assert_rnp_success(
- rnp_op_encrypt_add_password(op, "pass2", "SHA256", 12345, "BLOWFISH"));
+ const char *alg = blowfish_enabled() ? "BLOWFISH" : "AES256";
+ assert_rnp_success(rnp_op_encrypt_add_password(op, "pass2", "SHA256", 12345, alg));
} else if (!sm2_enabled() && twofish_enabled()) {
assert_rnp_failure(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH"));
assert_rnp_success(
rnp_op_encrypt_add_password(op, "pass2", "SHA256", 12345, "TWOFISH"));
} else if (sm2_enabled() && !twofish_enabled()) {
assert_rnp_failure(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH"));
- assert_rnp_success(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "BLOWFISH"));
+ const char *alg = blowfish_enabled() ? "BLOWFISH" : "AES256";
+ assert_rnp_success(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, alg));
} else {
assert_rnp_success(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH"));
}
@@ -598,7 +599,7 @@ TEST_F(rnp_tests, test_ffi_encrypt_pk)
rnp_ffi_destroy(ffi);
}
-bool
+static bool
first_key_password_provider(rnp_ffi_t ffi,
void * app_ctx,
rnp_key_handle_t key,
@@ -1360,3 +1361,29 @@ TEST_F(rnp_tests, test_ffi_encrypt_no_wrap)
// final cleanup
rnp_ffi_destroy(ffi);
}
+
+TEST_F(rnp_tests, test_ffi_mimemode_signature)
+{
+ rnp_ffi_t ffi = NULL;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ assert_true(import_pub_keys(ffi, "data/test_stream_key_load/ecc-25519-pub.asc"));
+
+ rnp_input_t input = NULL;
+ assert_rnp_success(
+ rnp_input_from_path(&input, "data/test_messages/message.txt.signed-mimemode"));
+ rnp_output_t output = NULL;
+ assert_rnp_success(rnp_output_to_null(&output));
+ rnp_op_verify_t verify = NULL;
+ assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output));
+ assert_rnp_success(rnp_op_verify_execute(verify));
+ size_t sigcount = 255;
+ assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount));
+ assert_int_equal(sigcount, 1);
+ rnp_op_verify_signature_t sig = NULL;
+ assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig));
+ assert_rnp_success(rnp_op_verify_signature_get_status(sig));
+ rnp_op_verify_destroy(verify);
+ rnp_input_destroy(input);
+ rnp_output_destroy(output);
+ rnp_ffi_destroy(ffi);
+}
diff --git a/src/tests/ffi-key-sig.cpp b/src/tests/ffi-key-sig.cpp
index 01bfdd2..1176de5 100644
--- a/src/tests/ffi-key-sig.cpp
+++ b/src/tests/ffi-key-sig.cpp
@@ -902,6 +902,11 @@ TEST_F(rnp_tests, test_ffi_sig_validity)
uint32_t expires = 0;
assert_rnp_success(rnp_signature_get_expiration(sig, &expires));
assert_int_equal(expires, 86400);
+ uint32_t features = 0;
+ assert_rnp_failure(rnp_signature_get_features(NULL, &features));
+ assert_rnp_failure(rnp_signature_get_features(sig, NULL));
+ assert_rnp_success(rnp_signature_get_features(sig, &features));
+ assert_int_equal(features, 0);
rnp_signature_handle_destroy(sig);
rnp_uid_handle_destroy(uid);
rnp_key_handle_destroy(key);
diff --git a/src/tests/ffi-key.cpp b/src/tests/ffi-key.cpp
index 2933d68..a25e6c4 100644
--- a/src/tests/ffi-key.cpp
+++ b/src/tests/ffi-key.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2022-2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -4440,3 +4440,28 @@ TEST_F(rnp_tests, test_reprotect_keys)
assert_rnp_success(rnp_identifier_iterator_destroy(it));
rnp_ffi_destroy(ffi);
}
+
+TEST_F(rnp_tests, test_armored_keys_extra_line)
+{
+ rnp_ffi_t ffi = NULL;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ /* Key with extra line after the checksum */
+ assert_true(
+ import_pub_keys(ffi, "data/test_stream_key_load/ecc-25519-pub-extra-line.asc"));
+ rnp_key_handle_t key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "cc786278981b0728", &key));
+ assert_true(check_key_valid(key, true));
+ assert_true(check_uid_valid(key, 0, true));
+ rnp_key_handle_destroy(key);
+
+ /* Key with extra lines with spaces after the checksum */
+ assert_true(
+ import_pub_keys(ffi, "data/test_stream_key_load/ecc-25519-pub-extra-line-2.asc"));
+ key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "cc786278981b0728", &key));
+ assert_true(check_key_valid(key, true));
+ assert_true(check_uid_valid(key, 0, true));
+ rnp_key_handle_destroy(key);
+
+ rnp_ffi_destroy(ffi);
+}
diff --git a/src/tests/ffi.cpp b/src/tests/ffi.cpp
index 1e75871..4c9f553 100644
--- a/src/tests/ffi.cpp
+++ b/src/tests/ffi.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2020 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -29,6 +29,7 @@
#include <string>
#include <set>
#include <utility>
+#include <cstdint>
#include <rnp/rnp.h>
#include "rnp_tests.h"
@@ -2659,7 +2660,8 @@ TEST_F(rnp_tests, test_ffi_revocations)
assert_rnp_failure(rnp_uid_is_revoked(uid_handle, NULL));
assert_rnp_success(rnp_uid_is_revoked(uid_handle, &revoked));
assert_false(revoked);
- rnp_signature_handle_t sig = (rnp_signature_handle_t) 0xdeadbeef;
+ const uintptr_t p_sig = 0xdeadbeef;
+ rnp_signature_handle_t sig = reinterpret_cast<rnp_signature_handle_t>(p_sig);
assert_rnp_failure(rnp_uid_get_revocation_signature(NULL, &sig));
assert_rnp_failure(rnp_uid_get_revocation_signature(uid_handle, NULL));
assert_rnp_success(rnp_uid_get_revocation_signature(uid_handle, &sig));
@@ -5951,11 +5953,16 @@ TEST_F(rnp_tests, test_ffi_security_profile)
assert_int_equal(flags, 0);
/* SHA1 - now, data verify disabled, key sig verify is enabled */
flags = 0;
- assert_rnp_success(rnp_get_security_rule(
- ffi, RNP_FEATURE_HASH_ALG, "SHA1", time(NULL), &flags, &from, &level));
- assert_int_equal(from, SHA1_DATA_FROM);
+ auto now = time(NULL);
+ bool sha1_cutoff = now > SHA1_KEY_FROM;
+ /* This would pick default rule closer to the date independent on usage */
+ assert_rnp_success(
+ rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, "SHA1", now, &flags, &from, &level));
+ auto expect_from = sha1_cutoff ? SHA1_KEY_FROM : SHA1_DATA_FROM;
+ auto expect_usage = sha1_cutoff ? RNP_SECURITY_VERIFY_KEY : RNP_SECURITY_VERIFY_DATA;
+ assert_int_equal(from, expect_from);
assert_int_equal(level, RNP_SECURITY_INSECURE);
- assert_int_equal(flags, RNP_SECURITY_VERIFY_DATA);
+ assert_int_equal(flags, expect_usage);
flags = 0;
assert_rnp_success(rnp_get_security_rule(
ffi, RNP_FEATURE_HASH_ALG, "SHA1", SHA1_DATA_FROM - 1, &flags, &from, &level));
@@ -5968,11 +5975,14 @@ TEST_F(rnp_tests, test_ffi_security_profile)
assert_int_equal(level, RNP_SECURITY_INSECURE);
assert_int_equal(flags, RNP_SECURITY_VERIFY_DATA);
flags = RNP_SECURITY_VERIFY_KEY;
- assert_rnp_success(rnp_get_security_rule(
- ffi, RNP_FEATURE_HASH_ALG, "SHA1", time(NULL), &flags, &from, &level));
- assert_int_equal(from, 0);
- assert_int_equal(level, RNP_SECURITY_DEFAULT);
- assert_int_equal(flags, 0);
+ assert_rnp_success(
+ rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, "SHA1", now, &flags, &from, &level));
+ expect_from = sha1_cutoff ? SHA1_KEY_FROM : 0;
+ auto expect_level = sha1_cutoff ? RNP_SECURITY_INSECURE : RNP_SECURITY_DEFAULT;
+ expect_usage = sha1_cutoff ? RNP_SECURITY_VERIFY_KEY : 0;
+ assert_int_equal(from, expect_from);
+ assert_int_equal(level, expect_level);
+ assert_int_equal(flags, expect_usage);
flags = RNP_SECURITY_VERIFY_KEY;
assert_rnp_success(rnp_get_security_rule(
ffi, RNP_FEATURE_HASH_ALG, "SHA1", SHA1_KEY_FROM + 5, &flags, &from, &level));
diff --git a/src/tests/generatekey.cpp b/src/tests/generatekey.cpp
index b846ebb..dd0aaff 100644
--- a/src/tests/generatekey.cpp
+++ b/src/tests/generatekey.cpp
@@ -96,12 +96,11 @@ hash_supported(const std::string &hash)
}
static bool
-hash_secure(rnp_ffi_t ffi, const std::string &hash, uint32_t action)
+hash_secure(rnp_ffi_t ffi, const std::string &hash, uint32_t action, uint64_t time)
{
uint32_t flags = action;
uint32_t level = 0;
- rnp_get_security_rule(
- ffi, RNP_FEATURE_HASH_ALG, hash.c_str(), global_ctx.time(), &flags, NULL, &level);
+ rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, hash.c_str(), time, &flags, NULL, &level);
return level == RNP_SECURITY_DEFAULT;
}
@@ -185,7 +184,8 @@ TEST_F(rnp_tests, rnpkeys_generatekey_testSignature)
cfg.set_bool(CFG_OVERWRITE, true);
cfg.set_str(CFG_INFILE, "dummyfile.dat.pgp");
cfg.set_str(CFG_OUTFILE, "dummyfile.verify");
- if (!hash_secure(rnp.ffi, hashAlg[i], RNP_SECURITY_VERIFY_DATA)) {
+ if (!hash_secure(
+ rnp.ffi, hashAlg[i], RNP_SECURITY_VERIFY_DATA, global_ctx.time())) {
assert_false(cli_rnp_process_file(&rnp));
rnp.end();
assert_int_equal(rnp_unlink("dummyfile.dat.pgp"), 0);
@@ -361,7 +361,7 @@ TEST_F(rnp_tests, rnpkeys_generatekey_verifySupportedHashAlg)
assert_true(keycount > 0);
rnp_key_handle_t handle = NULL;
assert_rnp_success(rnp_locate_key(rnp.ffi, "userid", hashAlg[i], &handle));
- if (hash_secure(rnp.ffi, hashAlg[i], RNP_SECURITY_VERIFY_KEY)) {
+ if (hash_secure(rnp.ffi, hashAlg[i], RNP_SECURITY_VERIFY_KEY, global_ctx.time())) {
assert_non_null(handle);
bool valid = false;
rnp_key_is_valid(handle, &valid);
diff --git a/src/tests/key-add-userid.cpp b/src/tests/key-add-userid.cpp
index b80dbb6..fba6ec0 100644
--- a/src/tests/key-add-userid.cpp
+++ b/src/tests/key-add-userid.cpp
@@ -69,6 +69,8 @@ TEST_F(rnp_tests, test_key_add_userid)
selfsig0.key_flags = 0x2;
selfsig0.key_expiration = base_expiry;
selfsig0.primary = false;
+ auto curtime = global_ctx.time();
+ global_ctx.set_time(curtime > SHA1_KEY_FROM ? SHA1_KEY_FROM - 100 : 0);
key->add_uid_cert(selfsig0, PGP_HASH_SHA1, global_ctx);
// attempt to add sha1-signed uid and make sure it succeeds now and fails after the cutoff
// date in 2024
diff --git a/src/tests/load-pgp.cpp b/src/tests/load-pgp.cpp
index 560ed3d..6583eee 100644
--- a/src/tests/load-pgp.cpp
+++ b/src/tests/load-pgp.cpp
@@ -124,9 +124,10 @@ TEST_F(rnp_tests, test_load_v4_keyring_pgp)
/* Just a helper for the below test */
static void
-check_pgp_keyring_counts(const char * path,
- unsigned primary_count,
- const unsigned subkey_counts[])
+check_pgp_keyring_counts(const char * path,
+ unsigned primary_count,
+ const unsigned subkey_counts[],
+ rnp::SecurityContext &global_ctx)
{
pgp_source_t src = {};
rnp_key_store_t *key_store = new rnp_key_store_t(global_ctx);
@@ -175,10 +176,12 @@ TEST_F(rnp_tests, test_load_keyring_and_count_pgp)
unsigned int subkey_counts[2] = {3, 2};
// check pubring
- check_pgp_keyring_counts("data/keyrings/1/pubring.gpg", primary_count, subkey_counts);
+ check_pgp_keyring_counts(
+ "data/keyrings/1/pubring.gpg", primary_count, subkey_counts, global_ctx);
// check secring
- check_pgp_keyring_counts("data/keyrings/1/secring.gpg", primary_count, subkey_counts);
+ check_pgp_keyring_counts(
+ "data/keyrings/1/secring.gpg", primary_count, subkey_counts, global_ctx);
}
/* This test loads a V4 keyring and confirms that certain
diff --git a/src/tests/rnp_tests.cpp b/src/tests/rnp_tests.cpp
index 910d2d8..6961da4 100644
--- a/src/tests/rnp_tests.cpp
+++ b/src/tests/rnp_tests.cpp
@@ -35,11 +35,6 @@
static char original_dir[PATH_MAX];
-/*
- * Handler used to access DRBG.
- */
-rnp::SecurityContext global_ctx;
-
#ifdef _WIN32
void
rnpInvalidParameterHandler(const wchar_t *expression,
diff --git a/src/tests/rnp_tests.h b/src/tests/rnp_tests.h
index 2dd43e9..58f99c8 100644
--- a/src/tests/rnp_tests.h
+++ b/src/tests/rnp_tests.h
@@ -38,7 +38,8 @@ class rnp_tests : public ::testing::Test {
const char *original_dir() const;
protected:
- char *m_dir;
+ char * m_dir;
+ rnp::SecurityContext global_ctx;
};
typedef struct {
diff --git a/src/tests/streams.cpp b/src/tests/streams.cpp
index c0bf698..3219387 100644
--- a/src/tests/streams.cpp
+++ b/src/tests/streams.cpp
@@ -1086,7 +1086,7 @@ TEST_F(rnp_tests, test_stream_key_signatures)
}
static bool
-validate_key_sigs(const char *path)
+validate_key_sigs(const char *path, rnp::SecurityContext &global_ctx)
{
rnp_key_store_t *pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, path, global_ctx);
bool valid = rnp_key_store_load_from_path(pubring, NULL);
@@ -1144,32 +1144,30 @@ TEST_F(rnp_tests, test_stream_key_signature_validate)
delete pubring;
/* misc key files */
- assert_true(validate_key_sigs("data/test_stream_key_load/dsa-eg-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/dsa-eg-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-25519-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-25519-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-x25519-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-x25519-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p256-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p256-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p384-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p384-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p521-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p521-sec.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp256-pub.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp256-sec.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp384-pub.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp384-sec.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp512-pub.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-bp512-sec.asc") ==
- brainpool_enabled());
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p256k1-pub.asc"));
- assert_true(validate_key_sigs("data/test_stream_key_load/ecc-p256k1-sec.asc"));
+ auto validate = [this](const std::string &file) {
+ auto path = "data/test_stream_key_load/" + file;
+ return validate_key_sigs(path.c_str(), this->global_ctx);
+ };
+ assert_true(validate("dsa-eg-pub.asc"));
+ assert_true(validate("dsa-eg-sec.asc"));
+ assert_true(validate("ecc-25519-pub.asc"));
+ assert_true(validate("ecc-25519-sec.asc"));
+ assert_true(validate("ecc-x25519-pub.asc"));
+ assert_true(validate("ecc-x25519-sec.asc"));
+ assert_true(validate("ecc-p256-pub.asc"));
+ assert_true(validate("ecc-p256-sec.asc"));
+ assert_true(validate("ecc-p384-pub.asc"));
+ assert_true(validate("ecc-p384-sec.asc"));
+ assert_true(validate("ecc-p521-pub.asc"));
+ assert_true(validate("ecc-p521-sec.asc"));
+ assert_true(validate("ecc-bp256-pub.asc") == brainpool_enabled());
+ assert_true(validate("ecc-bp256-sec.asc") == brainpool_enabled());
+ assert_true(validate("ecc-bp384-pub.asc") == brainpool_enabled());
+ assert_true(validate("ecc-bp384-sec.asc") == brainpool_enabled());
+ assert_true(validate("ecc-bp512-pub.asc") == brainpool_enabled());
+ assert_true(validate("ecc-bp512-sec.asc") == brainpool_enabled());
+ assert_true(validate("ecc-p256k1-pub.asc"));
+ assert_true(validate("ecc-p256k1-sec.asc"));
}
TEST_F(rnp_tests, test_stream_verify_no_key)
@@ -1568,11 +1566,15 @@ TEST_F(rnp_tests, test_stream_dearmor_edge_cases)
len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n%s\n", HDR, b64, CRC, FTR2);
assert_false(try_dearmor(msg, len));
- /* extra spaces or chars before the footer - FAIL */
+ /* extra spaces or tabs before the footer - allow it, see issue #2199 */
len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n %s\n", HDR, b64, CRC, FTR);
- assert_false(try_dearmor(msg, len));
+ assert_true(try_dearmor(msg, len));
len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n\t\t %s\n", HDR, b64, CRC, FTR);
+ assert_true(try_dearmor(msg, len));
+ /* no empty line between crc and footer - FAIL */
+ len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s%s\n", HDR, b64, CRC, FTR);
assert_false(try_dearmor(msg, len));
+ /* extra chars before the footer - FAIL */
len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n11111%s\n", HDR, b64, CRC, FTR);
assert_false(try_dearmor(msg, len));
@@ -1606,7 +1608,8 @@ TEST_F(rnp_tests, test_stream_dearmor_edge_cases)
}
static void
-add_openpgp_layers(const char *msg, pgp_dest_t &pgpdst, int compr, int encr)
+add_openpgp_layers(
+ const char *msg, pgp_dest_t &pgpdst, int compr, int encr, rnp::SecurityContext &global_ctx)
{
pgp_source_t src = {};
pgp_dest_t dst = {};
@@ -1648,7 +1651,7 @@ TEST_F(rnp_tests, test_stream_deep_packet_nesting)
pgp_dest_t dst = {};
/* add 30 compression layers and 2 encryption - must fail */
- add_openpgp_layers(message, dst, 30, 2);
+ add_openpgp_layers(message, dst, 30, 2, global_ctx);
#ifdef DUMP_TEST_CASE
/* remove ifdef if you want to write it to stdout */
pgp_source_t src = {};
@@ -1676,7 +1679,7 @@ TEST_F(rnp_tests, test_stream_deep_packet_nesting)
dst_close(&dst, false);
/* add 27 compression & 4 encryption layers - must succeed */
- add_openpgp_layers("message", dst, 27, 4);
+ add_openpgp_layers("message", dst, 27, 4, global_ctx);
#ifdef DUMP_TEST_CASE
/* remove ifdef if you want to write it to stdout */
assert_rnp_success(init_mem_src(&src, mem_dest_get_memory(&dst), dst.writeb, false));
diff --git a/src/tests/support.cpp b/src/tests/support.cpp
index c94a901..2867304 100644
--- a/src/tests/support.cpp
+++ b/src/tests/support.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-2023 [Ribose Inc](https://www.ribose.com).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -184,6 +184,7 @@ path_mkdir(mode_t mode, const char *first, ...)
assert_int_equal(0, RNP_MKDIR(buffer, mode));
}
+#ifndef WINSHELLAPI
static int
remove_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
@@ -193,6 +194,7 @@ remove_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ft
return ret;
}
+#endif
static const char *
get_tmp()
@@ -737,7 +739,9 @@ check_json_field_bool(json_object *obj, const std::string &field, bool value)
if (!json_object_is_type(fld, json_type_boolean)) {
return false;
}
- return json_object_get_boolean(fld) == value;
+ // 'json_object_get_boolean' returns 'json_bool' which is 'int' on Windows
+ // but bool on other platforms
+ return (json_object_get_boolean(fld) ? true : false) == value;
}
bool
diff --git a/src/tests/support.h b/src/tests/support.h
index 6206924..543a0d4 100644
--- a/src/tests/support.h
+++ b/src/tests/support.h
@@ -69,8 +69,6 @@ char *mkdtemp(char *templ);
#define realpath(N, R) _fullpath((R), (N), _MAX_PATH)
#endif
-extern rnp::SecurityContext global_ctx;
-
off_t file_size(const char *path);
/* Read file contents into the std::string */