diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 19:22:08 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 19:22:08 +0000 |
commit | 55fffa809312f6c0ae40528fd1ee66b7559c4650 (patch) | |
tree | bb6b3e63b35220d36bdc654f784a985e88ba1cd2 /src | |
parent | Releasing progress-linux version 0.17.0-3~progress7.99u1. (diff) | |
download | rnp-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')
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 Binary files differnew file mode 100644 index 0000000..4836610 --- /dev/null +++ b/src/tests/data/test_messages/message.aead-windows-issue diff --git a/src/tests/data/test_messages/message.aead-windows-issue-botan b/src/tests/data/test_messages/message.aead-windows-issue-botan Binary files differnew file mode 100644 index 0000000..84e17e8 --- /dev/null +++ b/src/tests/data/test_messages/message.aead-windows-issue-botan diff --git a/src/tests/data/test_messages/message.aead-windows-issue2 b/src/tests/data/test_messages/message.aead-windows-issue2 Binary files differnew file mode 100644 index 0000000..9af3d50 --- /dev/null +++ b/src/tests/data/test_messages/message.aead-windows-issue2 diff --git a/src/tests/data/test_messages/message.txt.signed-mimemode b/src/tests/data/test_messages/message.txt.signed-mimemode Binary files differnew file mode 100644 index 0000000..27f7579 --- /dev/null +++ b/src/tests/data/test_messages/message.txt.signed-mimemode 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 */ |