diff options
Diffstat (limited to 'src/lib')
-rwxr-xr-x | src/lib/CMakeLists.txt | 105 | ||||
-rw-r--r-- | src/lib/config.h.in | 4 | ||||
-rw-r--r-- | src/lib/crypto.cpp | 4 | ||||
-rw-r--r-- | src/lib/crypto/backend_version.cpp | 12 | ||||
-rw-r--r-- | src/lib/crypto/bn.h | 78 | ||||
-rw-r--r-- | src/lib/crypto/cipher.hpp | 5 | ||||
-rw-r--r-- | src/lib/crypto/cipher_botan.cpp | 8 | ||||
-rw-r--r-- | src/lib/crypto/dl_ossl.cpp | 118 | ||||
-rw-r--r-- | src/lib/crypto/dsa_ossl.cpp | 174 | ||||
-rw-r--r-- | src/lib/crypto/ec_ossl.cpp | 181 | ||||
-rw-r--r-- | src/lib/crypto/ecdh.cpp | 13 | ||||
-rw-r--r-- | src/lib/crypto/ecdh_ossl.cpp | 56 | ||||
-rw-r--r-- | src/lib/crypto/elgamal_ossl.cpp | 99 | ||||
-rw-r--r-- | src/lib/crypto/rsa.cpp | 8 | ||||
-rw-r--r-- | src/lib/crypto/rsa_ossl.cpp | 351 | ||||
-rw-r--r-- | src/lib/crypto/symmetric.cpp | 12 | ||||
-rw-r--r-- | src/lib/crypto/symmetric_ossl.cpp | 52 | ||||
-rw-r--r-- | src/lib/rnp.cpp | 23 |
18 files changed, 983 insertions, 320 deletions
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); |