Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor pgp_key_material_t to C++. #2250

Merged
merged 8 commits into from
Aug 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/windows-msys2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ jobs:
mkdir -p "build/Testing/Temporary"
cp "cmake/CTestCostData.txt" "build/Testing/Temporary"
export PATH="$PWD/build/src/lib:$PATH"
export RNP_LOG_CONSOLE=1
ctest --parallel ${{ env.CORES }} --test-dir build -C Debug --output-on-failure

- name: Install
Expand Down
6 changes: 5 additions & 1 deletion src/fuzzing/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020 Ribose Inc.
# Copyright (c) 2020, 2024 Ribose Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
Expand All @@ -22,6 +22,7 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

find_package(Botan 2.14.0 REQUIRED)

if(NOT DEFINED ENV{LIB_FUZZING_ENGINE})
add_compile_options(-fsanitize=fuzzer-no-link)
Expand Down Expand Up @@ -127,6 +128,8 @@ target_link_libraries(fuzz_keyring_kbx

add_executable(fuzz_keyring_g10 keyring_g10.cpp)

set_target_properties(fuzz_keyring_g10 PROPERTIES CXX_STANDARD 20)

target_include_directories(fuzz_keyring_g10
PRIVATE
"${PROJECT_SOURCE_DIR}/src"
Expand All @@ -136,6 +139,7 @@ target_include_directories(fuzz_keyring_g10
target_link_libraries(fuzz_keyring_g10
PRIVATE
librnp-static
Botan::Botan
)

if (ENABLE_SANITIZERS)
Expand Down
7 changes: 4 additions & 3 deletions src/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ if(ENABLE_PQC)
endif()

# check that AEAD is enabled and not turned off for ENABLE_CRYPTO_REFRESH
if(ENABLE_CRYPTO_REFRESH AND (NOT ENABLE_AEAD))
message(FATAL_ERROR "ENABLE_CRYPTO_REFRESH requires ENABLE_AEAD, but it's either Off or Auto and got turned off")
endif()
if(ENABLE_CRYPTO_REFRESH AND (NOT ENABLE_AEAD))
message(FATAL_ERROR "ENABLE_CRYPTO_REFRESH requires ENABLE_AEAD, but it's either Off or Auto and got turned off")
endif()

# generate a config.h
include(CheckIncludeFileCXX)
Expand Down Expand Up @@ -370,6 +370,7 @@ add_library(librnp-obj OBJECT
json-utils.cpp
utils.cpp
pass-provider.cpp
key_material.cpp
pgp-key.cpp
rnp.cpp
)
Expand Down
278 changes: 7 additions & 271 deletions src/lib/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,286 +95,22 @@
#endif
seckey.creation_time = crypto.ctx->time();
seckey.alg = crypto.key_alg;
seckey.material.alg = crypto.key_alg;
seckey.material = pgp::KeyMaterial::create(crypto.key_alg);
if (!seckey.material) {
RNP_LOG("Unsupported key algorithm: %d", crypto.key_alg);
return false;

Check warning on line 101 in src/lib/crypto.cpp

View check run for this annotation

Codecov / codecov/patch

src/lib/crypto.cpp#L100-L101

Added lines #L100 - L101 were not covered by tests
}
seckey.tag = primary ? PGP_PKT_SECRET_KEY : PGP_PKT_SECRET_SUBKEY;

switch (seckey.alg) {
case PGP_PKA_RSA:
if (rsa_generate(&crypto.ctx->rng, &seckey.material.rsa, crypto.rsa.modulus_bit_len)) {
RNP_LOG("failed to generate RSA key");
return false;
}
break;
case PGP_PKA_DSA:
if (dsa_generate(&crypto.ctx->rng,
&seckey.material.dsa,
crypto.dsa.p_bitlen,
crypto.dsa.q_bitlen)) {
RNP_LOG("failed to generate DSA key");
return false;
}
break;
case PGP_PKA_EDDSA:
if (eddsa_generate(&crypto.ctx->rng, &seckey.material.ec)) {
RNP_LOG("failed to generate EDDSA key");
return false;
}
break;
case PGP_PKA_ECDH:
if (!ecdh_set_params(&seckey.material.ec, crypto.ecc.curve)) {
RNP_LOG("Unsupported curve [ID=%d]", crypto.ecc.curve);
return false;
}
if (crypto.ecc.curve == PGP_CURVE_25519) {
if (x25519_generate(&crypto.ctx->rng, &seckey.material.ec)) {
RNP_LOG("failed to generate x25519 key");
return false;
}
seckey.material.ec.curve = crypto.ecc.curve;
break;
}
FALLTHROUGH_STATEMENT;
case PGP_PKA_ECDSA:
case PGP_PKA_SM2:
if (!curve_supported(crypto.ecc.curve)) {
RNP_LOG("EC generate: curve %d is not supported.", (int) crypto.ecc.curve);
return false;
}
if (ec_generate(&crypto.ctx->rng, &seckey.material.ec, seckey.alg, crypto.ecc.curve)) {
RNP_LOG("failed to generate EC key");
return false;
}
seckey.material.ec.curve = crypto.ecc.curve;
break;
case PGP_PKA_ELGAMAL:
if (elgamal_generate(
&crypto.ctx->rng, &seckey.material.eg, crypto.elgamal.key_bitlen)) {
RNP_LOG("failed to generate ElGamal key");
return false;
}
break;
#if defined(ENABLE_CRYPTO_REFRESH)
case PGP_PKA_ED25519:
if (generate_ed25519_native(&crypto.ctx->rng,
seckey.material.ed25519.priv,
seckey.material.ed25519.pub) != RNP_SUCCESS) {
RNP_LOG("failed to generate ED25519 key");
return false;
}
break;
case PGP_PKA_X25519:
if (generate_x25519_native(&crypto.ctx->rng,
seckey.material.x25519.priv,
seckey.material.x25519.pub) != RNP_SUCCESS) {
RNP_LOG("failed to generate X25519 key");
return false;
}
break;
#endif
#if defined(ENABLE_PQC)
case PGP_PKA_KYBER768_X25519:
FALLTHROUGH_STATEMENT;
// TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER768_P256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER1024_P384:
FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER768_BP256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER1024_BP384:
if (pgp_kyber_ecdh_composite_key_t::gen_keypair(
&crypto.ctx->rng, &seckey.material.kyber_ecdh, seckey.alg)) {
RNP_LOG("failed to generate MLKEM-ECDH-composite key for PK alg %d", seckey.alg);
return false;
}
break;
case PGP_PKA_DILITHIUM3_ED25519:
FALLTHROUGH_STATEMENT;
// TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM3_P256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM5_P384:
FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM3_BP256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM5_BP384:
if (pgp_dilithium_exdsa_composite_key_t::gen_keypair(
&crypto.ctx->rng, &seckey.material.dilithium_exdsa, seckey.alg)) {
RNP_LOG("failed to generate mldsa-ecdsa/eddsa-composite key for PK alg %d",
seckey.alg);
return false;
}
break;
case PGP_PKA_SPHINCSPLUS_SHA2:
FALLTHROUGH_STATEMENT;
case PGP_PKA_SPHINCSPLUS_SHAKE:
if (pgp_sphincsplus_generate(&crypto.ctx->rng,
&seckey.material.sphincsplus,
crypto.sphincsplus.param,
seckey.alg)) {
RNP_LOG("failed to generate SLH-DSA key for PK alg %d", seckey.alg);
return false;
}
break;
#endif
default:
RNP_LOG("key generation not implemented for PK alg: %d", seckey.alg);
if (!seckey.material->generate(crypto)) {
return false;
}

seckey.sec_protection.s2k.usage = PGP_S2KU_NONE;
seckey.material.secret = true;
seckey.material.validity.mark_valid();
/* fill the sec_data/sec_len */
if (encrypt_secret_key(&seckey, NULL, crypto.ctx->rng)) {
RNP_LOG("failed to fill sec_data");
return false;
}
return true;
}

bool
key_material_equal(const pgp_key_material_t *key1, const pgp_key_material_t *key2)
{
if (key1->alg != key2->alg) {
return false;
}

switch (key1->alg) {
case PGP_PKA_RSA:
case PGP_PKA_RSA_ENCRYPT_ONLY:
case PGP_PKA_RSA_SIGN_ONLY:
return (key1->rsa.n == key2->rsa.n) && (key1->rsa.e == key2->rsa.e);
case PGP_PKA_DSA:
return (key1->dsa.p == key2->dsa.p) && (key1->dsa.q == key2->dsa.q) &&
(key1->dsa.g == key2->dsa.g) && (key1->dsa.y == key2->dsa.y);
case PGP_PKA_ELGAMAL:
case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
return (key1->eg.p == key2->eg.p) && (key1->eg.g == key2->eg.g) &&
(key1->eg.y == key2->eg.y);
case PGP_PKA_EDDSA:
case PGP_PKA_ECDH:
case PGP_PKA_ECDSA:
case PGP_PKA_SM2:
return (key1->ec.curve == key2->ec.curve) && (key1->ec.p == key2->ec.p);
#if defined(ENABLE_CRYPTO_REFRESH)
case PGP_PKA_ED25519:
return (key1->ed25519.pub == key2->ed25519.pub);
case PGP_PKA_X25519:
return (key1->x25519.pub == key2->x25519.pub);
#endif
#if defined(ENABLE_PQC)
case PGP_PKA_KYBER768_X25519:
FALLTHROUGH_STATEMENT;
// TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER768_P256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER1024_P384:
FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER768_BP256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER1024_BP384:
return (key1->kyber_ecdh.pub == key2->kyber_ecdh.pub);
case PGP_PKA_DILITHIUM3_ED25519:
FALLTHROUGH_STATEMENT;
// TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM3_P256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM5_P384:
FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM3_BP256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM5_BP384:
return (key1->dilithium_exdsa.pub == key2->dilithium_exdsa.pub);
case PGP_PKA_SPHINCSPLUS_SHA2:
FALLTHROUGH_STATEMENT;
case PGP_PKA_SPHINCSPLUS_SHAKE:
return (key1->sphincsplus.pub == key2->sphincsplus.pub);
#endif
default:
RNP_LOG("unknown public key algorithm: %d", (int) key1->alg);
return false;
}
}

rnp_result_t
validate_pgp_key_material(const pgp_key_material_t *material, rnp::RNG *rng)
{
#ifdef FUZZERS_ENABLED
/* do not timeout on large keys during fuzzing */
return RNP_SUCCESS;
#else
switch (material->alg) {
case PGP_PKA_RSA:
case PGP_PKA_RSA_ENCRYPT_ONLY:
case PGP_PKA_RSA_SIGN_ONLY:
return rsa_validate_key(rng, &material->rsa, material->secret);
case PGP_PKA_DSA:
return dsa_validate_key(rng, &material->dsa, material->secret);
case PGP_PKA_EDDSA:
return eddsa_validate_key(rng, &material->ec, material->secret);
case PGP_PKA_ECDH:
if (!curve_supported(material->ec.curve)) {
/* allow to import key if curve is not supported */
RNP_LOG("ECDH validate: curve %d is not supported.", (int) material->ec.curve);
return RNP_SUCCESS;
}
return ecdh_validate_key(rng, &material->ec, material->secret);
case PGP_PKA_ECDSA:
if (!curve_supported(material->ec.curve)) {
/* allow to import key if curve is not supported */
RNP_LOG("ECDH validate: curve %d is not supported.", (int) material->ec.curve);
return RNP_SUCCESS;
}
return ecdsa_validate_key(rng, &material->ec, material->secret);
case PGP_PKA_SM2:
#if defined(ENABLE_SM2)
return sm2_validate_key(rng, &material->ec, material->secret);
#else
RNP_LOG("SM2 key validation is not available.");
return RNP_ERROR_NOT_IMPLEMENTED;
#endif
case PGP_PKA_ELGAMAL:
case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
return elgamal_validate_key(&material->eg, material->secret) ? RNP_SUCCESS :
RNP_ERROR_GENERIC;
#if defined(ENABLE_CRYPTO_REFRESH)
case PGP_PKA_ED25519:
return ed25519_validate_key_native(rng, &material->ed25519, material->secret);
case PGP_PKA_X25519:
return x25519_validate_key_native(rng, &material->x25519, material->secret);
#endif
#if defined(ENABLE_PQC)
case PGP_PKA_KYBER768_X25519:
FALLTHROUGH_STATEMENT;
// TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER768_P256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER1024_P384:
FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER768_BP256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER1024_BP384:
return kyber_ecdh_validate_key(rng, &material->kyber_ecdh, material->secret);
case PGP_PKA_DILITHIUM3_ED25519:
FALLTHROUGH_STATEMENT;
// TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM3_P256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM5_P384:
FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM3_BP256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_DILITHIUM5_BP384:
return dilithium_exdsa_validate_key(rng, &material->dilithium_exdsa, material->secret);
case PGP_PKA_SPHINCSPLUS_SHA2:
FALLTHROUGH_STATEMENT;
case PGP_PKA_SPHINCSPLUS_SHAKE:
return sphincsplus_validate_key(rng, &material->sphincsplus, material->secret);
#endif
default:
RNP_LOG("unknown public key algorithm: %d", (int) material->alg);
}

return RNP_ERROR_BAD_PARAMETERS;
#endif
}
12 changes: 0 additions & 12 deletions src/lib/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,4 @@ bool pgp_generate_subkey(rnp_keygen_subkey_desc_t & desc,
const pgp_password_provider_t &password_provider,
pgp_key_store_format_t secformat);

/**
* @brief Check two key material for equality. Only public part is checked, so this can be
* called on public/secret key material
*
* @param key1 first key material
* @param key2 second key material
* @return true if both key materials are equal or false otherwise
*/
bool key_material_equal(const pgp_key_material_t *key1, const pgp_key_material_t *key2);

rnp_result_t validate_pgp_key_material(const pgp_key_material_t *material, rnp::RNG *rng);

#endif /* CRYPTO_H_ */
11 changes: 11 additions & 0 deletions src/lib/crypto/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ typedef struct pgp_dsa_key_t {
pgp::mpi y;
/* secret mpi */
pgp::mpi x;

void
clear_secret()
{
x.forget();
}

~pgp_dsa_key_t()
{
clear_secret();
}
} pgp_dsa_key_t;

typedef struct pgp_dsa_signature_t {
Expand Down
Loading