From c42055cf86c23ceeb08c3117d3f7459e0f5d5c35 Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Mon, 30 Sep 2024 14:13:29 +0200 Subject: [PATCH 01/15] change parametrized SPHINCS+ to separate SPHINCS+ algorithms --- include/repgp/repgp_def.h | 5 +- include/rnp/rnp.h | 36 +- src/lib/crypto/sphincsplus.cpp | 296 ++++------------ src/lib/crypto/sphincsplus.h | 78 +---- src/lib/generate-key.cpp | 604 +++++++++++++++++++++++++++++++++ src/lib/key_material.cpp | 44 +-- src/lib/key_material.hpp | 17 +- src/lib/keygen.cpp | 15 +- src/lib/pgp-key.cpp | 12 +- src/lib/rnp.cpp | 102 ++---- src/librepgp/stream-dump.cpp | 29 +- src/librepgp/stream-sig.cpp | 29 +- src/rnp/fficli.cpp | 31 -- src/rnp/rnpcfg.h | 8 - src/rnpkeys/tui.cpp | 84 +---- src/tests/cipher.cpp | 59 ++-- src/tests/cli_tests.py | 9 +- src/tests/ffi.cpp | 2 +- src/tests/pqc.cpp | 36 +- 19 files changed, 841 insertions(+), 655 deletions(-) create mode 100644 src/lib/generate-key.cpp diff --git a/include/repgp/repgp_def.h b/include/repgp/repgp_def.h index 524d9c62ed..851831a6e8 100644 --- a/include/repgp/repgp_def.h +++ b/include/repgp/repgp_def.h @@ -249,8 +249,9 @@ typedef enum : uint8_t { PGP_PKA_DILITHIUM3_BP256 = 117, PGP_PKA_DILITHIUM5_BP384 = 118, - PGP_PKA_SPHINCSPLUS_SHA2 = 109, - PGP_PKA_SPHINCSPLUS_SHAKE = 119, + PGP_PKA_SPHINCSPLUS_SHAKE_128f = 119, + PGP_PKA_SPHINCSPLUS_SHAKE_128s = 120, + PGP_PKA_SPHINCSPLUS_SHAKE_256s = 121, PGP_PKA_PRIVATE00 = 100, /* Private/Experimental Algorithm */ PGP_PKA_PRIVATE01 = 101, /* Private/Experimental Algorithm */ diff --git a/include/rnp/rnp.h b/include/rnp/rnp.h index d2d7930d8a..c305805ae8 100644 --- a/include/rnp/rnp.h +++ b/include/rnp/rnp.h @@ -1218,24 +1218,6 @@ RNP_API rnp_result_t rnp_op_generate_set_pref_keyserver(rnp_op_generate_t op, RNP_API rnp_result_t rnp_op_generate_set_v6_key(rnp_op_generate_t op); #endif -#if defined(RNP_EXPERIMENTAL_PQC) -/** Set the SPHINCS+ parameter set - * NOTE: This is an experimental feature and this function can be replaced (or removed) at any - * time. - * - * @param op pointer to opaque key generation context. - * @param param string, representing the SHPINCS+ parameter set. - * Possible Values: - * 128s, 128f, 192s, 192f, 256s, 256f - * All parameter sets refer to the simple variant and the hash function is given - * by the algorithm id. - * - * @return RNP_SUCCESS or error code if failed. - */ -RNP_API rnp_result_t rnp_op_generate_set_sphincsplus_param(rnp_op_generate_t op, - const char * param); -#endif - /** Execute the prepared key or subkey generation operation. * Note: if you set protection algorithm, then you need to specify ffi password provider to * be able to request password for key encryption. @@ -2375,19 +2357,6 @@ RNP_API rnp_result_t rnp_key_get_default_key(rnp_key_handle_t primary_key, */ RNP_API rnp_result_t rnp_key_get_alg(rnp_key_handle_t key, char **alg); -#if defined(RNP_EXPERIMENTAL_PQC) -/** Get a SPHINCS+ key's parameter string - * - * @param key key handle - * @param alg string with parameter name will be stored here. You must free it using the - * rnp_buffer_destroy() function. - * @return RNP_SUCCESS or error code if failed. - * NOTE: This is an experimental feature and this function can be replaced (or removed) at any - * time. - */ -RNP_API rnp_result_t rnp_key_sphincsplus_get_param(rnp_key_handle_t handle, char **param); -#endif - /** Get number of bits in the key. For EC-based keys it will return size of the curve. * * @param key key handle @@ -4058,8 +4027,9 @@ RNP_API const char *rnp_backend_version(); #define RNP_ALGNAME_DILITHIUM5_P384 "ML-DSA-87+ECDSA-P384" #define RNP_ALGNAME_DILITHIUM3_BP256 "ML-DSA-65+ECDSA-BP256" #define RNP_ALGNAME_DILITHIUM5_BP384 "ML-DSA-87+ECDSA-BP384" -#define RNP_ALGNAME_SPHINCSPLUS_SHA2 "SLH-DSA-SHA2" -#define RNP_ALGNAME_SPHINCSPLUS_SHAKE "SLH-DSA-SHAKE" +#define RNP_ALGNAME_SPHINCSPLUS_SHAKE_128f "SLH-DSA-SHAKE-128f" +#define RNP_ALGNAME_SPHINCSPLUS_SHAKE_128s "SLH-DSA-SHAKE-128s" +#define RNP_ALGNAME_SPHINCSPLUS_SHAKE_256s "SLH-DSA-SHAKE-256s" #endif #define RNP_ALGNAME_IDEA "IDEA" #define RNP_ALGNAME_TRIPLEDES "TRIPLEDES" diff --git a/src/lib/crypto/sphincsplus.cpp b/src/lib/crypto/sphincsplus.cpp index 7b2ef80af6..3f673ff849 100644 --- a/src/lib/crypto/sphincsplus.cpp +++ b/src/lib/crypto/sphincsplus.cpp @@ -31,120 +31,48 @@ namespace { Botan::Sphincs_Parameter_Set -rnp_sphincsplus_params_to_botan_param(sphincsplus_parameter_t param) +rnp_sphincsplus_alg_to_botan_param(pgp_pubkey_alg_t alg) { - switch (param) { - case sphincsplus_simple_128s: - return Botan::Sphincs_Parameter_Set::Sphincs128Small; - case sphincsplus_simple_128f: + switch (alg) { + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: return Botan::Sphincs_Parameter_Set::Sphincs128Fast; - case sphincsplus_simple_192s: - return Botan::Sphincs_Parameter_Set::Sphincs192Small; - case sphincsplus_simple_192f: - return Botan::Sphincs_Parameter_Set::Sphincs192Fast; - case sphincsplus_simple_256s: + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + return Botan::Sphincs_Parameter_Set::Sphincs128Small; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: return Botan::Sphincs_Parameter_Set::Sphincs256Small; - case sphincsplus_simple_256f: - return Botan::Sphincs_Parameter_Set::Sphincs256Fast; - default: - RNP_LOG("invalid parameter given"); - throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); - } -} -Botan::Sphincs_Hash_Type -rnp_sphincsplus_hash_func_to_botan_hash_func(sphincsplus_hash_func_t hash_func) -{ - switch (hash_func) { - case sphincsplus_sha256: - return Botan::Sphincs_Hash_Type::Sha256; - case sphinscplus_shake256: - return Botan::Sphincs_Hash_Type::Shake256; - default: - RNP_LOG("invalid parameter given"); - throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); - } -} - -sphincsplus_hash_func_t -rnp_sphincsplus_alg_to_hashfunc(pgp_pubkey_alg_t alg) -{ - switch (alg) { - case PGP_PKA_SPHINCSPLUS_SHA2: - return sphincsplus_sha256; - case PGP_PKA_SPHINCSPLUS_SHAKE: - return sphinscplus_shake256; default: - RNP_LOG("invalid SLH-DSA alg id"); - throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); - } -} - -pgp_pubkey_alg_t -rnp_sphincsplus_hashfunc_to_alg(sphincsplus_hash_func_t hashfunc) -{ - switch (hashfunc) { - case sphincsplus_sha256: - return PGP_PKA_SPHINCSPLUS_SHA2; - case sphinscplus_shake256: - return PGP_PKA_SPHINCSPLUS_SHAKE; - default: - RNP_LOG("invalid SLH-DSA hashfunc"); + RNP_LOG("invalid algorithm ID given"); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); } } } // namespace -pgp_sphincsplus_public_key_t::pgp_sphincsplus_public_key_t(const uint8_t *key_encoded, - size_t key_encoded_len, - sphincsplus_parameter_t param, - sphincsplus_hash_func_t hash_func) - : key_encoded_(key_encoded, key_encoded + key_encoded_len), - pk_alg_(rnp_sphincsplus_hashfunc_to_alg(hash_func)), sphincsplus_param_(param), - sphincsplus_hash_func_(hash_func), is_initialized_(true) +pgp_sphincsplus_public_key_t::pgp_sphincsplus_public_key_t(const uint8_t * key_encoded, + size_t key_encoded_len, + pgp_pubkey_alg_t alg) + : key_encoded_(key_encoded, key_encoded + key_encoded_len), pk_alg_(alg), + is_initialized_(true) { } pgp_sphincsplus_public_key_t::pgp_sphincsplus_public_key_t( - std::vector const &key_encoded, - sphincsplus_parameter_t param, - sphincsplus_hash_func_t hash_func) - : key_encoded_(key_encoded), pk_alg_(rnp_sphincsplus_hashfunc_to_alg(hash_func)), - sphincsplus_param_(param), sphincsplus_hash_func_(hash_func), is_initialized_(true) + std::vector const &key_encoded, pgp_pubkey_alg_t alg) + : key_encoded_(key_encoded), pk_alg_(alg), is_initialized_(true) { } -pgp_sphincsplus_public_key_t::pgp_sphincsplus_public_key_t( - std::vector const &key_encoded, sphincsplus_parameter_t param, pgp_pubkey_alg_t alg) - : key_encoded_(key_encoded), pk_alg_(alg), sphincsplus_param_(param), - sphincsplus_hash_func_(rnp_sphincsplus_alg_to_hashfunc(alg)), is_initialized_(true) -{ -} - -pgp_sphincsplus_private_key_t::pgp_sphincsplus_private_key_t(const uint8_t *key_encoded, - size_t key_encoded_len, - sphincsplus_parameter_t param, - sphincsplus_hash_func_t hash_func) - : key_encoded_(key_encoded, key_encoded + key_encoded_len), - pk_alg_(rnp_sphincsplus_hashfunc_to_alg(hash_func)), sphincsplus_param_(param), - sphincsplus_hash_func_(hash_func), is_initialized_(true) +pgp_sphincsplus_private_key_t::pgp_sphincsplus_private_key_t(const uint8_t * key_encoded, + size_t key_encoded_len, + pgp_pubkey_alg_t alg) + : key_encoded_(key_encoded, key_encoded + key_encoded_len), pk_alg_(alg), + is_initialized_(true) { } pgp_sphincsplus_private_key_t::pgp_sphincsplus_private_key_t( - std::vector const &key_encoded, - sphincsplus_parameter_t param, - sphincsplus_hash_func_t hash_func) + std::vector const &key_encoded, pgp_pubkey_alg_t alg) : key_encoded_(Botan::secure_vector(key_encoded.begin(), key_encoded.end())), - pk_alg_(rnp_sphincsplus_hashfunc_to_alg(hash_func)), sphincsplus_param_(param), - sphincsplus_hash_func_(hash_func), is_initialized_(true) -{ -} - -pgp_sphincsplus_private_key_t::pgp_sphincsplus_private_key_t( - std::vector const &key_encoded, sphincsplus_parameter_t param, pgp_pubkey_alg_t alg) - : key_encoded_(Botan::secure_vector(key_encoded.begin(), key_encoded.end())), - pk_alg_(alg), sphincsplus_param_(param), - sphincsplus_hash_func_(rnp_sphincsplus_alg_to_hashfunc(alg)), is_initialized_(true) + pk_alg_(alg), is_initialized_(true) { } @@ -159,7 +87,6 @@ pgp_sphincsplus_private_key_t::sign(rnp::RNG * rng, auto signer = Botan::PK_Signer(priv_key, *rng->obj(), ""); sig->sig = signer.sign_message(msg, msg_len, *rng->obj()); - sig->param = param(); return RNP_SUCCESS; } @@ -167,10 +94,9 @@ pgp_sphincsplus_private_key_t::sign(rnp::RNG * rng, Botan::SphincsPlus_PublicKey pgp_sphincsplus_public_key_t::botan_key() const { - return Botan::SphincsPlus_PublicKey( - key_encoded_, - rnp_sphincsplus_params_to_botan_param(this->sphincsplus_param_), - rnp_sphincsplus_hash_func_to_botan_hash_func(this->sphincsplus_hash_func_)); + return Botan::SphincsPlus_PublicKey(key_encoded_, + rnp_sphincsplus_alg_to_botan_param(this->pk_alg_), + Botan::Sphincs_Hash_Type::Shake256); } Botan::SphincsPlus_PrivateKey @@ -178,10 +104,9 @@ pgp_sphincsplus_private_key_t::botan_key() const { Botan::secure_vector priv_sv(key_encoded_.data(), key_encoded_.data() + key_encoded_.size()); - return Botan::SphincsPlus_PrivateKey( - priv_sv, - rnp_sphincsplus_params_to_botan_param(this->sphincsplus_param_), - rnp_sphincsplus_hash_func_to_botan_hash_func(this->sphincsplus_hash_func_)); + return Botan::SphincsPlus_PrivateKey(priv_sv, + rnp_sphincsplus_alg_to_botan_param(this->pk_alg_), + Botan::Sphincs_Hash_Type::Shake256); } rnp_result_t @@ -200,32 +125,23 @@ pgp_sphincsplus_public_key_t::verify(const pgp_sphincsplus_signature_t *sig, } std::pair -sphincsplus_generate_keypair(rnp::RNG * rng, - sphincsplus_parameter_t sphincsplus_param, - sphincsplus_hash_func_t sphincsplus_hash_func) +sphincsplus_generate_keypair(rnp::RNG *rng, pgp_pubkey_alg_t alg) { - Botan::SphincsPlus_PrivateKey priv_key( - *rng->obj(), - rnp_sphincsplus_params_to_botan_param(sphincsplus_param), - rnp_sphincsplus_hash_func_to_botan_hash_func(sphincsplus_hash_func)); + Botan::SphincsPlus_PrivateKey priv_key(*rng->obj(), + rnp_sphincsplus_alg_to_botan_param(alg), + Botan::Sphincs_Hash_Type::Shake256); std::unique_ptr pub_key = priv_key.public_key(); Botan::secure_vector priv_bits = priv_key.private_key_bits(); return std::make_pair( - pgp_sphincsplus_public_key_t( - pub_key->public_key_bits(), sphincsplus_param, sphincsplus_hash_func), - pgp_sphincsplus_private_key_t( - priv_bits.data(), priv_bits.size(), sphincsplus_param, sphincsplus_hash_func)); + pgp_sphincsplus_public_key_t(pub_key->public_key_bits(), alg), + pgp_sphincsplus_private_key_t(priv_bits.data(), priv_bits.size(), alg)); } rnp_result_t -pgp_sphincsplus_generate(rnp::RNG * rng, - pgp_sphincsplus_key_t * material, - sphincsplus_parameter_t param, - pgp_pubkey_alg_t alg) +pgp_sphincsplus_generate(rnp::RNG *rng, pgp_sphincsplus_key_t *material, pgp_pubkey_alg_t alg) { - auto keypair = - sphincsplus_generate_keypair(rng, param, rnp_sphincsplus_alg_to_hashfunc(alg)); + auto keypair = sphincsplus_generate_keypair(rng, alg); material->pub = keypair.first; material->priv = keypair.second; @@ -237,7 +153,7 @@ pgp_sphincsplus_public_key_t::validate_signature_hash_requirements( pgp_hash_alg_t hash_alg) const { /* check if key is allowed with the hash algorithm */ - return sphincsplus_hash_allowed(pk_alg_, sphincsplus_param_, hash_alg); + return sphincsplus_hash_allowed(pk_alg_, hash_alg); } bool @@ -279,26 +195,20 @@ sphincsplus_validate_key(rnp::RNG *rng, const pgp_sphincsplus_key_t *key, bool s } size_t -sphincsplus_privkey_size(sphincsplus_parameter_t param) +sphincsplus_privkey_size(pgp_pubkey_alg_t alg) { - return 2 * sphincsplus_pubkey_size(param); + return 2 * sphincsplus_pubkey_size(alg); } size_t -sphincsplus_pubkey_size(sphincsplus_parameter_t param) +sphincsplus_pubkey_size(pgp_pubkey_alg_t alg) { - switch (param) { - case sphincsplus_simple_128s: + switch (alg) { + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: return 32; - case sphincsplus_simple_128f: + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: return 32; - case sphincsplus_simple_192s: - return 48; - case sphincsplus_simple_192f: - return 48; - case sphincsplus_simple_256s: - return 64; - case sphincsplus_simple_256f: + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: return 64; default: RNP_LOG("invalid SLH-DSA parameter identifier"); @@ -307,21 +217,15 @@ sphincsplus_pubkey_size(sphincsplus_parameter_t param) } size_t -sphincsplus_signature_size(sphincsplus_parameter_t param) +sphincsplus_signature_size(pgp_pubkey_alg_t alg) { - switch (param) { - case sphincsplus_simple_128s: - return 7856; - case sphincsplus_simple_128f: + switch (alg) { + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: return 17088; - case sphincsplus_simple_192s: - return 16224; - case sphincsplus_simple_192f: - return 35664; - case sphincsplus_simple_256s: + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + return 7856; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: return 29792; - case sphincsplus_simple_256f: - return 49856; default: RNP_LOG("invalid SLH-DSA parameter identifier"); return 0; @@ -329,97 +233,33 @@ sphincsplus_signature_size(sphincsplus_parameter_t param) } bool -sphincsplus_hash_allowed(pgp_pubkey_alg_t pk_alg, - sphincsplus_parameter_t sphincsplus_param, - pgp_hash_alg_t hash_alg) +sphincsplus_hash_allowed(pgp_pubkey_alg_t pk_alg, pgp_hash_alg_t hash_alg) { - /* draft-wussler-openpgp-pqc-02 Table 14*/ switch (pk_alg) { - case PGP_PKA_SPHINCSPLUS_SHA2: - switch (sphincsplus_param) { - case sphincsplus_simple_128s: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_128f: - if (hash_alg != PGP_HASH_SHA256) { - return false; - } - break; - case sphincsplus_simple_192s: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_192f: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_256s: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_256f: - if (hash_alg != PGP_HASH_SHA512) { - return false; - } - break; - } - break; - case PGP_PKA_SPHINCSPLUS_SHAKE: - switch (sphincsplus_param) { - case sphincsplus_simple_128s: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_128f: - if (hash_alg != PGP_HASH_SHA3_256) { - return false; - } - break; - case sphincsplus_simple_192s: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_192f: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_256s: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_256f: - if (hash_alg != PGP_HASH_SHA3_512) { - return false; - } - break; - } - break; + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + return hash_alg == PGP_HASH_SHA3_256; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: + return hash_alg == PGP_HASH_SHA3_512; default: - break; + RNP_LOG("invalid algorithm ID given"); + throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); } - return true; } pgp_hash_alg_t -sphincsplus_default_hash_alg(pgp_pubkey_alg_t pk_alg, - sphincsplus_parameter_t sphincsplus_param) +sphincsplus_default_hash_alg(pgp_pubkey_alg_t alg) { - switch (sphincsplus_param) { - case sphincsplus_simple_128s: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_128f: - switch (pk_alg) { - case PGP_PKA_SPHINCSPLUS_SHA2: - return PGP_HASH_SHA256; - case PGP_PKA_SPHINCSPLUS_SHAKE: - return PGP_HASH_SHA3_256; - default: - RNP_LOG("invalid parameter given"); - throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); - } - case sphincsplus_simple_192s: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_192f: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_256s: - FALLTHROUGH_STATEMENT; - case sphincsplus_simple_256f: - switch (pk_alg) { - case PGP_PKA_SPHINCSPLUS_SHA2: - return PGP_HASH_SHA512; - case PGP_PKA_SPHINCSPLUS_SHAKE: - return PGP_HASH_SHA3_512; - default: - RNP_LOG("invalid parameter given"); - throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); - } + switch (alg) { + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + return PGP_HASH_SHA3_256; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + return PGP_HASH_SHA3_256; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: + return PGP_HASH_SHA3_512; default: - RNP_LOG("invalid parameter given"); + RNP_LOG("invalid algorithm ID given"); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); } -} +} \ No newline at end of file diff --git a/src/lib/crypto/sphincsplus.h b/src/lib/crypto/sphincsplus.h index 95300f09a4..ff9204164d 100644 --- a/src/lib/crypto/sphincsplus.h +++ b/src/lib/crypto/sphincsplus.h @@ -38,55 +38,27 @@ struct pgp_sphincsplus_key_t; struct pgp_sphincsplus_signature_t; -typedef enum { sphincsplus_sha256, sphinscplus_shake256 } sphincsplus_hash_func_t; -typedef enum : uint8_t { - sphincsplus_simple_128s = 1, - sphincsplus_simple_128f = 2, - sphincsplus_simple_192s = 3, - sphincsplus_simple_192f = 4, - sphincsplus_simple_256s = 5, - sphincsplus_simple_256f = 6 -} sphincsplus_parameter_t; - typedef struct pgp_sphincsplus_signature_t { std::vector sig; - sphincsplus_parameter_t param; } pgp_sphincsplus_signature_t; class pgp_sphincsplus_private_key_t { public: - pgp_sphincsplus_private_key_t(const uint8_t * key_encoded, - size_t key_encoded_len, - sphincsplus_parameter_t sphincs_param, - sphincsplus_hash_func_t sphincs_hash_func); - pgp_sphincsplus_private_key_t(std::vector const &key_encoded, - sphincsplus_parameter_t sphincs_param, - sphincsplus_hash_func_t sphincs_hash_func); + pgp_sphincsplus_private_key_t(const uint8_t *key_encoded, + size_t key_encoded_len, + pgp_pubkey_alg_t alg); pgp_sphincsplus_private_key_t(std::vector const &key_encoded, - sphincsplus_parameter_t param, pgp_pubkey_alg_t alg); pgp_sphincsplus_private_key_t() = default; bool is_valid(rnp::RNG *rng) const; - sphincsplus_parameter_t - param() const - { - return sphincsplus_param_; - } - pgp_pubkey_alg_t alg() const { return pk_alg_; } - sphincsplus_hash_func_t - hash_func() const - { - return sphincsplus_hash_func_; - } - rnp_result_t sign(rnp::RNG * rng, pgp_sphincsplus_signature_t *sig, const uint8_t * msg, @@ -109,31 +81,22 @@ class pgp_sphincsplus_private_key_t { Botan::secure_vector key_encoded_; pgp_pubkey_alg_t pk_alg_; - sphincsplus_parameter_t sphincsplus_param_; - sphincsplus_hash_func_t sphincsplus_hash_func_; bool is_initialized_ = false; }; class pgp_sphincsplus_public_key_t { public: - pgp_sphincsplus_public_key_t(const uint8_t * key_encoded, - size_t key_encoded_len, - sphincsplus_parameter_t sphincs_param, - sphincsplus_hash_func_t sphincs_hash_func); - pgp_sphincsplus_public_key_t(std::vector const &key_encoded, - sphincsplus_parameter_t sphincs_param, - sphincsplus_hash_func_t sphincs_hash_func); + pgp_sphincsplus_public_key_t(const uint8_t * key_encoded, + size_t key_encoded_len, + pgp_pubkey_alg_t alg); pgp_sphincsplus_public_key_t(std::vector const &key_encoded, - sphincsplus_parameter_t param, pgp_pubkey_alg_t alg); pgp_sphincsplus_public_key_t() = default; bool operator==(const pgp_sphincsplus_public_key_t &rhs) const { - return (sphincsplus_param_ == rhs.sphincsplus_param_) && - (sphincsplus_hash_func_ == rhs.sphincsplus_hash_func_) && - (key_encoded_ == rhs.key_encoded_); + return (pk_alg_ == rhs.pk_alg_) && (key_encoded_ == rhs.key_encoded_); } rnp_result_t verify(const pgp_sphincsplus_signature_t *sig, @@ -144,12 +107,6 @@ class pgp_sphincsplus_public_key_t { bool validate_signature_hash_requirements(pgp_hash_alg_t hash_alg) const; - sphincsplus_parameter_t - param() const - { - return sphincsplus_param_; - } - pgp_pubkey_alg_t alg() const { @@ -167,19 +124,14 @@ class pgp_sphincsplus_public_key_t { std::vector key_encoded_; pgp_pubkey_alg_t pk_alg_; - sphincsplus_parameter_t sphincsplus_param_; - sphincsplus_hash_func_t sphincsplus_hash_func_; bool is_initialized_ = false; }; std::pair -sphincsplus_generate_keypair(rnp::RNG * rng, - sphincsplus_parameter_t sphincs_param, - sphincsplus_hash_func_t sphincs_hash_func); +sphincsplus_generate_keypair(rnp::RNG *rng, pgp_pubkey_alg_t alg); rnp_result_t pgp_sphincsplus_generate(rnp::RNG * rng, pgp_sphincsplus_key_t * material, - sphincsplus_parameter_t param, pgp_pubkey_alg_t alg); rnp_result_t sphincsplus_validate_key(rnp::RNG * rng, @@ -191,15 +143,13 @@ typedef struct pgp_sphincsplus_key_t { pgp_sphincsplus_private_key_t priv; } pgp_sphincsplus_key_t; -size_t sphincsplus_privkey_size(sphincsplus_parameter_t param); -size_t sphincsplus_pubkey_size(sphincsplus_parameter_t param); -size_t sphincsplus_signature_size(sphincsplus_parameter_t param); +size_t sphincsplus_privkey_size(pgp_pubkey_alg_t alg); +size_t sphincsplus_pubkey_size(pgp_pubkey_alg_t alg); +size_t sphincsplus_signature_size(pgp_pubkey_alg_t alg); -bool sphincsplus_hash_allowed(pgp_pubkey_alg_t pk_alg, - sphincsplus_parameter_t sphincsplus_param, - pgp_hash_alg_t hash_alg); +bool sphincsplus_hash_allowed(pgp_pubkey_alg_t pk_alg, + pgp_hash_alg_t hash_alg); -pgp_hash_alg_t sphincsplus_default_hash_alg(pgp_pubkey_alg_t pk_alg, - sphincsplus_parameter_t sphincsplus_param); +pgp_hash_alg_t sphincsplus_default_hash_alg(pgp_pubkey_alg_t pk_alg); #endif \ No newline at end of file diff --git a/src/lib/generate-key.cpp b/src/lib/generate-key.cpp new file mode 100644 index 0000000000..5255bed7b8 --- /dev/null +++ b/src/lib/generate-key.cpp @@ -0,0 +1,604 @@ +/* + * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include + +#include +#include +#include +#include "crypto.h" +#include "pgp-key.h" +#include "defaults.h" +#include "utils.h" + +static const uint8_t DEFAULT_SYMMETRIC_ALGS[] = { + PGP_SA_AES_256, PGP_SA_AES_192, PGP_SA_AES_128}; +static const uint8_t DEFAULT_HASH_ALGS[] = { + PGP_HASH_SHA256, PGP_HASH_SHA384, PGP_HASH_SHA512, PGP_HASH_SHA224}; +static const uint8_t DEFAULT_COMPRESS_ALGS[] = { + PGP_C_ZLIB, PGP_C_BZIP2, PGP_C_ZIP, PGP_C_NONE}; +#if defined(ENABLE_CRYPTO_REFRESH) +static const uint8_t DEFAULT_AEAD_ALGS[] = {PGP_AEAD_OCB}; +#endif + +static const id_str_pair pubkey_alg_map[] = { + {PGP_PKA_RSA, "RSA (Encrypt or Sign)"}, + {PGP_PKA_RSA_ENCRYPT_ONLY, "RSA Encrypt-Only"}, + {PGP_PKA_RSA_SIGN_ONLY, "RSA Sign-Only"}, + {PGP_PKA_ELGAMAL, "Elgamal (Encrypt-Only)"}, + {PGP_PKA_DSA, "DSA"}, + {PGP_PKA_ECDH, "ECDH"}, + {PGP_PKA_ECDSA, "ECDSA"}, + {PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN, "Reserved (formerly Elgamal Encrypt or Sign"}, + {PGP_PKA_RESERVED_DH, "Reserved for Diffie-Hellman (X9.42)"}, + {PGP_PKA_EDDSA, "EdDSA"}, + {PGP_PKA_SM2, "SM2"}, +#if defined(ENABLE_CRYPTO_REFRESH) + {PGP_PKA_ED25519, "ED25519"}, + {PGP_PKA_X25519, "X25519"}, +#endif +#if defined(ENABLE_PQC) + {PGP_PKA_KYBER768_X25519, "ML-KEM-768_X25519"}, + //{PGP_PKA_KYBER1024_X448, "Kyber-X448"}, + {PGP_PKA_KYBER768_P256, "ML-KEM-768_P256"}, + {PGP_PKA_KYBER1024_P384, "ML-KEM-1024_P384"}, + {PGP_PKA_KYBER768_BP256, "ML-KEM-768_BP256"}, + {PGP_PKA_KYBER1024_BP384, "ML-KEM-1024_BP384"}, + {PGP_PKA_DILITHIUM3_ED25519, "ML-DSA-65_ED25519"}, + //{PGP_PKA_DILITHIUM5_ED448, "Dilithium-ED448"}, + {PGP_PKA_DILITHIUM3_P256, "ML-DSA-65_P256"}, + {PGP_PKA_DILITHIUM5_P384, "ML-DSA-87_P384"}, + {PGP_PKA_DILITHIUM3_BP256, "ML-DSA-65_BP256"}, + {PGP_PKA_DILITHIUM5_BP384, "ML-DSA-87_BP384"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_128f, "SLH-DSA-SHAKE-128f"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_128s, "SLH-DSA-SHAKE-128s"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_256s, "SLH-DSA-SHAKE-256s"}, + {PGP_PKA_PRIVATE00, "Private/Experimental"}, + {PGP_PKA_PRIVATE01, "Private/Experimental"}, + {PGP_PKA_PRIVATE02, "Private/Experimental"}, + {PGP_PKA_PRIVATE03, "Private/Experimental"}, + {PGP_PKA_PRIVATE04, "Private/Experimental"}, + {PGP_PKA_PRIVATE06, "Private/Experimental"}, + {PGP_PKA_PRIVATE08, "Private/Experimental"}, + {PGP_PKA_PRIVATE10, "Private/Experimental"}, +#else + {PGP_PKA_PRIVATE00, "Private/Experimental"}, + {PGP_PKA_PRIVATE01, "Private/Experimental"}, + {PGP_PKA_PRIVATE02, "Private/Experimental"}, + {PGP_PKA_PRIVATE03, "Private/Experimental"}, + {PGP_PKA_PRIVATE04, "Private/Experimental"}, + {PGP_PKA_PRIVATE05, "Private/Experimental"}, + {PGP_PKA_PRIVATE06, "Private/Experimental"}, + {PGP_PKA_PRIVATE07, "Private/Experimental"}, + {PGP_PKA_PRIVATE08, "Private/Experimental"}, + {PGP_PKA_PRIVATE09, "Private/Experimental"}, + {PGP_PKA_PRIVATE10, "Private/Experimental"}, +#endif + {0, NULL}}; + +static bool +load_generated_g10_key(pgp_key_t * dst, + pgp_key_pkt_t * newkey, + pgp_key_t * primary_key, + pgp_key_t * pubkey, + rnp::SecurityContext &ctx) +{ + // this should generally be zeroed + assert(dst->type() == 0); + // if a primary is provided, make sure it's actually a primary key + assert(!primary_key || primary_key->is_primary()); + // if a pubkey is provided, make sure it's actually a public key + assert(!pubkey || pubkey->is_public()); + // G10 always needs pubkey here + assert(pubkey); + + // this would be better on the stack but the key store does not allow it + std::unique_ptr key_store(new (std::nothrow) rnp::KeyStore(ctx)); + if (!key_store) { + return false; + } + /* Write g10 seckey */ + rnp::MemoryDest memdst(NULL, 0); + if (!g10_write_seckey(&memdst.dst(), newkey, NULL, ctx)) { + RNP_LOG("failed to write generated seckey"); + return false; + } + + std::vector key_ptrs; /* holds primary and pubkey, when used */ + // if this is a subkey, add the primary in first + if (primary_key) { + key_ptrs.push_back(primary_key); + } + // G10 needs the pubkey for copying some attributes (key version, creation time, etc) + key_ptrs.push_back(pubkey); + + rnp::MemorySource memsrc(memdst.memory(), memdst.writeb(), false); + rnp::KeyProvider prov(rnp_key_provider_key_ptr_list, &key_ptrs); + if (!key_store.get()->load_g10(memsrc.src(), &prov)) { + return false; + } + if (key_store.get()->key_count() != 1) { + return false; + } + // if a primary key is provided, it should match the sub with regards to type + assert(!primary_key || (primary_key->is_secret() == key_store->keys.front().is_secret())); + *dst = pgp_key_t(key_store->keys.front()); + return true; +} + +static uint8_t +pk_alg_default_flags(pgp_pubkey_alg_t alg) +{ + // just use the full capabilities as the ultimate fallback + return pgp_pk_alg_capabilities(alg); +} + +// TODO: Similar as pgp_pick_hash_alg but different enough to +// keep another version. This will be changed when refactoring crypto +static void +adjust_hash_alg(rnp_keygen_crypto_params_t &crypto) +{ + if (!crypto.hash_alg) { + crypto.hash_alg = (pgp_hash_alg_t) DEFAULT_HASH_ALGS[0]; + } + + if ((crypto.key_alg != PGP_PKA_DSA) && (crypto.key_alg != PGP_PKA_ECDSA)) { + return; + } + + pgp_hash_alg_t min_hash = (crypto.key_alg == PGP_PKA_ECDSA) ? + ecdsa_get_min_hash(crypto.ecc.curve) : + dsa_get_min_hash(crypto.dsa.q_bitlen); + + if (rnp::Hash::size(crypto.hash_alg) < rnp::Hash::size(min_hash)) { + crypto.hash_alg = min_hash; + } +} + +static void +keygen_merge_crypto_defaults(rnp_keygen_crypto_params_t &crypto) +{ + // default to RSA + if (!crypto.key_alg) { + crypto.key_alg = PGP_PKA_RSA; + } + + switch (crypto.key_alg) { + case PGP_PKA_RSA: + if (!crypto.rsa.modulus_bit_len) { + crypto.rsa.modulus_bit_len = DEFAULT_RSA_NUMBITS; + } + break; + + case PGP_PKA_SM2: + if (!crypto.hash_alg) { + crypto.hash_alg = PGP_HASH_SM3; + } + if (!crypto.ecc.curve) { + crypto.ecc.curve = PGP_CURVE_SM2_P_256; + } + break; + + case PGP_PKA_ECDH: + case PGP_PKA_ECDSA: { + if (!crypto.hash_alg) { + crypto.hash_alg = (pgp_hash_alg_t) DEFAULT_HASH_ALGS[0]; + } + break; + } + + case PGP_PKA_EDDSA: + if (!crypto.ecc.curve) { + crypto.ecc.curve = PGP_CURVE_ED25519; + } + break; + + case PGP_PKA_DSA: { + if (!crypto.dsa.p_bitlen) { + crypto.dsa.p_bitlen = DSA_DEFAULT_P_BITLEN; + } + if (!crypto.dsa.q_bitlen) { + crypto.dsa.q_bitlen = dsa_choose_qsize_by_psize(crypto.dsa.p_bitlen); + } + break; + } + default: + break; + } + + adjust_hash_alg(crypto); +} + +static bool +validate_keygen_primary(const rnp_keygen_primary_desc_t &desc) +{ + /* Confirm that the specified pk alg can certify. + * gpg requires this, though the RFC only says that a V4 primary + * key SHOULD be a key capable of certification. + */ + if (!(pgp_pk_alg_capabilities(desc.crypto.key_alg) & PGP_KF_CERTIFY)) { + RNP_LOG("primary key alg (%d) must be able to sign", desc.crypto.key_alg); + return false; + } + + // check key flags + if (!desc.cert.key_flags) { + // these are probably not *technically* required + RNP_LOG("key flags are required"); + return false; + } else if (desc.cert.key_flags & ~pgp_pk_alg_capabilities(desc.crypto.key_alg)) { + // check the flags against the alg capabilities + RNP_LOG("usage not permitted for pk algorithm"); + return false; + } + // require a userid + if (!desc.cert.userid[0]) { + RNP_LOG("userid is required for primary key"); + return false; + } + return true; +} + +static uint32_t +get_numbits(const rnp_keygen_crypto_params_t *crypto) +{ + switch (crypto->key_alg) { + case PGP_PKA_RSA: + case PGP_PKA_RSA_ENCRYPT_ONLY: + case PGP_PKA_RSA_SIGN_ONLY: + return crypto->rsa.modulus_bit_len; + case PGP_PKA_ECDSA: + case PGP_PKA_ECDH: + case PGP_PKA_EDDSA: + case PGP_PKA_SM2: { + if (const ec_curve_desc_t *curve = get_curve_desc(crypto->ecc.curve)) { + return curve->bitlen; + } else { + return 0; + } + } +#if defined(ENABLE_CRYPTO_REFRESH) + case PGP_PKA_ED25519: + return 255; + case PGP_PKA_X25519: + return 255; +#endif + case PGP_PKA_DSA: + return crypto->dsa.p_bitlen; + case PGP_PKA_ELGAMAL: + case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN: + return crypto->elgamal.key_bitlen; +#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 pgp_kyber_ecdh_composite_public_key_t::encoded_size(crypto->key_alg) * 8; + 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 pgp_dilithium_exdsa_composite_public_key_t::encoded_size(crypto->key_alg) * 8; + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: + return sphincsplus_pubkey_size(crypto->key_alg) * 8; +#endif + default: + return 0; + } +} + +static void +set_default_user_prefs(pgp_user_prefs_t &prefs) +{ + if (prefs.symm_algs.empty()) { + prefs.set_symm_algs( + std::vector(DEFAULT_SYMMETRIC_ALGS, + DEFAULT_SYMMETRIC_ALGS + ARRAY_SIZE(DEFAULT_SYMMETRIC_ALGS))); + } + if (prefs.hash_algs.empty()) { + prefs.set_hash_algs(std::vector( + DEFAULT_HASH_ALGS, DEFAULT_HASH_ALGS + ARRAY_SIZE(DEFAULT_HASH_ALGS))); + } + if (prefs.z_algs.empty()) { + prefs.set_z_algs(std::vector( + DEFAULT_COMPRESS_ALGS, DEFAULT_COMPRESS_ALGS + ARRAY_SIZE(DEFAULT_COMPRESS_ALGS))); + } +#if defined(ENABLE_CRYPTO_REFRESH) + if (prefs.aead_prefs.empty()) { + std::vector algs; + for (auto aead_alg : DEFAULT_AEAD_ALGS) { + for (auto sym_alg : prefs.symm_algs) { + algs.push_back(sym_alg); + algs.push_back(aead_alg); + } + } + prefs.set_aead_prefs(algs); + } +#endif +} + +static void +keygen_primary_merge_defaults(rnp_keygen_primary_desc_t &desc) +{ + keygen_merge_crypto_defaults(desc.crypto); + set_default_user_prefs(desc.cert.prefs); + + if (!desc.cert.key_flags) { + // set some default key flags if none are provided + desc.cert.key_flags = pk_alg_default_flags(desc.crypto.key_alg); + } + if (desc.cert.userid.empty()) { + char uid[MAX_ID_LENGTH] = {0}; + snprintf(uid, + sizeof(uid), + "%s %d-bit key <%s@localhost>", + id_str_pair::lookup(pubkey_alg_map, desc.crypto.key_alg), + get_numbits(&desc.crypto), + getenv_logname()); + desc.cert.userid = uid; + } +} + +#if defined(ENABLE_PQC) +static bool +pgp_check_key_hash_requirements(rnp_keygen_crypto_params_t &crypto) +{ + switch (crypto.key_alg) { + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: + if (!sphincsplus_hash_allowed(crypto.key_alg, crypto.hash_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 (!dilithium_hash_allowed(crypto.hash_alg)) { + return false; + } + break; + default: + break; + } + return true; +} +#endif + +bool +pgp_generate_primary_key(rnp_keygen_primary_desc_t &desc, + bool merge_defaults, + pgp_key_t & primary_sec, + pgp_key_t & primary_pub, + pgp_key_store_format_t secformat) +{ + // validate args + if (primary_sec.type() || primary_pub.type()) { + RNP_LOG("invalid parameters (should be zeroed)"); + return false; + } + + try { + // merge some defaults in, if requested + if (merge_defaults) { + keygen_primary_merge_defaults(desc); + } + // now validate the keygen fields + if (!validate_keygen_primary(desc)) { + return false; + } + +#if defined(ENABLE_PQC) + // check hash requirements + if (!pgp_check_key_hash_requirements(desc.crypto)) { + RNP_LOG("invalid hash algorithm for the chosen key"); + return false; + } +#endif + + // generate the raw key and fill tag/secret fields + pgp_key_pkt_t secpkt; + if (!pgp_generate_seckey(desc.crypto, secpkt, true, desc.pgp_version)) { + return false; + } + + pgp_key_t sec(secpkt); + pgp_key_t pub(secpkt, true); +#if defined(ENABLE_CRYPTO_REFRESH) + // for v6 packets, a direct-key sig is mandatory. + if (sec.version() == PGP_V6) { + sec.add_direct_sig(desc.cert, desc.crypto.hash_alg, *desc.crypto.ctx, &pub); + } +#endif + sec.add_uid_cert(desc.cert, desc.crypto.hash_alg, *desc.crypto.ctx, &pub); + + switch (secformat) { + case PGP_KEY_STORE_GPG: + case PGP_KEY_STORE_KBX: + primary_sec = std::move(sec); + primary_pub = std::move(pub); + break; + case PGP_KEY_STORE_G10: + primary_pub = std::move(pub); + if (!load_generated_g10_key( + &primary_sec, &secpkt, NULL, &primary_pub, *desc.crypto.ctx)) { + RNP_LOG("failed to load generated key"); + return false; + } + break; + default: + RNP_LOG("invalid format"); + return false; + } + } catch (const std::exception &e) { + RNP_LOG("Failure: %s", e.what()); + return false; + } + + /* mark it as valid */ + primary_pub.mark_valid(); + primary_sec.mark_valid(); + /* refresh key's data */ + return primary_pub.refresh_data(*desc.crypto.ctx) && + primary_sec.refresh_data(*desc.crypto.ctx); +} + +static bool +validate_keygen_subkey(rnp_keygen_subkey_desc_t &desc) +{ + if (!desc.binding.key_flags) { + RNP_LOG("key flags are required"); + return false; + } else if (desc.binding.key_flags & ~pgp_pk_alg_capabilities(desc.crypto.key_alg)) { + // check the flags against the alg capabilities + RNP_LOG("usage not permitted for pk algorithm"); + return false; + } + return true; +} + +static void +keygen_subkey_merge_defaults(rnp_keygen_subkey_desc_t &desc) +{ + keygen_merge_crypto_defaults(desc.crypto); + if (!desc.binding.key_flags) { + // set some default key flags if none are provided + desc.binding.key_flags = pk_alg_default_flags(desc.crypto.key_alg); + } +} + +bool +pgp_generate_subkey(rnp_keygen_subkey_desc_t & desc, + bool merge_defaults, + pgp_key_t & primary_sec, + pgp_key_t & primary_pub, + pgp_key_t & subkey_sec, + pgp_key_t & subkey_pub, + const pgp_password_provider_t &password_provider, + pgp_key_store_format_t secformat) +{ + // validate args + if (!primary_sec.is_primary() || !primary_pub.is_primary() || !primary_sec.is_secret() || + !primary_pub.is_public()) { + RNP_LOG("invalid parameters"); + return false; + } + if (subkey_sec.type() || subkey_pub.type()) { + RNP_LOG("invalid parameters (should be zeroed)"); + return false; + } + + // merge some defaults in, if requested + if (merge_defaults) { + keygen_subkey_merge_defaults(desc); + } + + // now validate the keygen fields + if (!validate_keygen_subkey(desc)) { + return false; + } + +#if defined(ENABLE_PQC) + // check hash requirements + if (!pgp_check_key_hash_requirements(desc.crypto)) { + RNP_LOG("invalid hash algorithm for the chosen key"); + return false; + } +#endif + + try { + /* decrypt the primary seckey if needed (for signatures) */ + rnp::KeyLocker primlock(primary_sec); + if (primary_sec.encrypted() && + !primary_sec.unlock(password_provider, PGP_OP_ADD_SUBKEY)) { + RNP_LOG("Failed to unlock primary key."); + return false; + } + /* generate the raw subkey */ + pgp_key_pkt_t secpkt; + if (!pgp_generate_seckey(desc.crypto, secpkt, false, desc.pgp_version)) { + return false; + } + pgp_key_pkt_t pubpkt = pgp_key_pkt_t(secpkt, true); + pgp_key_t sec(secpkt, primary_sec); + pgp_key_t pub(pubpkt, primary_pub); + /* add binding */ + primary_sec.add_sub_binding( + sec, pub, desc.binding, desc.crypto.hash_alg, *desc.crypto.ctx); + /* copy to the result */ + subkey_pub = std::move(pub); + switch (secformat) { + case PGP_KEY_STORE_GPG: + case PGP_KEY_STORE_KBX: + subkey_sec = std::move(sec); + break; + case PGP_KEY_STORE_G10: + if (!load_generated_g10_key( + &subkey_sec, &secpkt, &primary_sec, &subkey_pub, *desc.crypto.ctx)) { + RNP_LOG("failed to load generated key"); + return false; + } + break; + default: + RNP_LOG("invalid format"); + return false; + } + + subkey_pub.mark_valid(); + subkey_sec.mark_valid(); + return subkey_pub.refresh_data(&primary_pub, *desc.crypto.ctx) && + subkey_sec.refresh_data(&primary_sec, *desc.crypto.ctx); + } catch (const std::exception &e) { + RNP_LOG("Subkey generation failed: %s", e.what()); + return false; + } +} diff --git a/src/lib/key_material.cpp b/src/lib/key_material.cpp index 43f81519eb..1a00baf882 100644 --- a/src/lib/key_material.cpp +++ b/src/lib/key_material.cpp @@ -181,10 +181,12 @@ KeyParams::create(pgp_pubkey_alg_t alg) FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: return std::unique_ptr(new DilithiumEccKeyParams(alg)); - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: - return std::unique_ptr(new SlhdsaKeyParams()); + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: + return std::unique_ptr(new SlhdsaKeyParams(alg)); #endif default: throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); @@ -234,7 +236,7 @@ DilithiumEccKeyParams::bits() const noexcept size_t SlhdsaKeyParams::bits() const noexcept { - return sphincsplus_pubkey_size(param_) * 8; + return sphincsplus_pubkey_size(alg_) * 8; } #endif @@ -433,9 +435,11 @@ KeyMaterial::create(pgp_pubkey_alg_t alg) FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: return std::unique_ptr(new DilithiumEccKeyMaterial(alg)); - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: return std::unique_ptr(new SlhdsaKeyMaterial(alg)); #endif default: @@ -1930,15 +1934,9 @@ bool SlhdsaKeyMaterial::parse(pgp_packet_body_t &pkt) noexcept { secret_ = false; - uint8_t bt = 0; - if (!pkt.get(bt)) { - RNP_LOG("failed to parse SLH-DSA public key data"); - return false; - } - sphincsplus_parameter_t param = (sphincsplus_parameter_t) bt; - auto size = sphincsplus_pubkey_size(param); + auto size = sphincsplus_pubkey_size(alg()); if (!size) { - RNP_LOG("invalid SLH-DSA param"); + RNP_LOG("invalid algorithm"); return false; } std::vector buf(size); @@ -1946,25 +1944,19 @@ SlhdsaKeyMaterial::parse(pgp_packet_body_t &pkt) noexcept RNP_LOG("failed to parse SLH-DSA public key data"); return false; } - key_.pub = pgp_sphincsplus_public_key_t(buf, param, alg()); + key_.pub = pgp_sphincsplus_public_key_t(buf, alg()); return true; } bool SlhdsaKeyMaterial::parse_secret(pgp_packet_body_t &pkt) noexcept { - uint8_t bt = 0; - if (!pkt.get(bt)) { - RNP_LOG("failed to parse SLH-DSA secret key data"); - return false; - } - sphincsplus_parameter_t param = (sphincsplus_parameter_t) bt; - std::vector buf(sphincsplus_privkey_size(param)); + std::vector buf(sphincsplus_privkey_size(alg())); if (!pkt.get(buf.data(), buf.size())) { RNP_LOG("failed to parse SLH-DSA secret key data"); return false; } - key_.priv = pgp_sphincsplus_private_key_t(buf, param, alg()); + key_.priv = pgp_sphincsplus_private_key_t(buf, alg()); secret_ = true; return true; } @@ -1972,14 +1964,12 @@ SlhdsaKeyMaterial::parse_secret(pgp_packet_body_t &pkt) noexcept void SlhdsaKeyMaterial::write(pgp_packet_body_t &pkt) const { - pkt.add_byte((uint8_t) key_.pub.param()); pkt.add(key_.pub.get_encoded()); } void SlhdsaKeyMaterial::write_secret(pgp_packet_body_t &pkt) const { - pkt.add_byte((uint8_t) key_.priv.param()); pkt.add(key_.priv.get_encoded()); } @@ -1987,7 +1977,7 @@ bool SlhdsaKeyMaterial::generate(rnp::SecurityContext &ctx, const KeyParams ¶ms) { auto &slhdsa = dynamic_cast(params); - if (pgp_sphincsplus_generate(&ctx.rng, &key_, slhdsa.param(), alg_)) { + if (pgp_sphincsplus_generate(&ctx.rng, &key_, alg_)) { RNP_LOG("failed to generate SLH-DSA key for PK alg %d", alg_); return false; } @@ -2013,7 +2003,7 @@ SlhdsaKeyMaterial::sign(rnp::SecurityContext & ctx, pgp_hash_alg_t SlhdsaKeyMaterial::adjust_hash(pgp_hash_alg_t hash) const { - return sphincsplus_default_hash_alg(alg_, key_.pub.param()); + return sphincsplus_default_hash_alg(alg()); } bool diff --git a/src/lib/key_material.hpp b/src/lib/key_material.hpp index 9a03e9915f..06f244f5c7 100644 --- a/src/lib/key_material.hpp +++ b/src/lib/key_material.hpp @@ -156,23 +156,10 @@ class DilithiumEccKeyParams : public KeyParams { class SlhdsaKeyParams : public KeyParams { private: - sphincsplus_parameter_t param_; + pgp_pubkey_alg_t alg_; public: - SlhdsaKeyParams() : param_(sphincsplus_simple_128f){}; - - sphincsplus_parameter_t - param() const noexcept - { - return param_; - } - - void - set_param(sphincsplus_parameter_t value) noexcept - { - param_ = value; - } - + SlhdsaKeyParams(pgp_pubkey_alg_t alg) : alg_(alg){}; size_t bits() const noexcept override; }; #endif diff --git a/src/lib/keygen.cpp b/src/lib/keygen.cpp index e3d47bfe89..3e132560b9 100644 --- a/src/lib/keygen.cpp +++ b/src/lib/keygen.cpp @@ -54,16 +54,16 @@ KeygenParams::validate() const noexcept { #if defined(ENABLE_PQC) switch (alg()) { - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: { - auto &slhdsa = dynamic_cast(key_params()); - if (!sphincsplus_hash_allowed(alg(), slhdsa.param(), hash())) { + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: + if (!sphincsplus_hash_allowed(alg(), hash())) { RNP_LOG("invalid hash algorithm for the slhdsa key"); return false; } break; - } case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; @@ -158,8 +158,9 @@ static const id_str_pair pubkey_alg_map[] = {{PGP_PKA_RSA, "RSA (Encrypt or Sign {PGP_PKA_DILITHIUM5_P384, "ML-DSA-87_P384"}, {PGP_PKA_DILITHIUM3_BP256, "ML-DSA-65_BP256"}, {PGP_PKA_DILITHIUM5_BP384, "ML-DSA-87_BP384"}, - {PGP_PKA_SPHINCSPLUS_SHA2, "SLH-DSA-SHA2"}, - {PGP_PKA_SPHINCSPLUS_SHAKE, "SLH-DSA-SHAKE"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_128f, "SLH-DSA-SHAKE-128f"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_128s, "SLH-DSA-SHAKE-128s"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_256s, "SLH-DSA-SHAKE-256s"}, #endif {0, NULL}}; diff --git a/src/lib/pgp-key.cpp b/src/lib/pgp-key.cpp index fa4c694d47..0bb3239da7 100644 --- a/src/lib/pgp-key.cpp +++ b/src/lib/pgp-key.cpp @@ -196,9 +196,11 @@ pgp_pk_alg_capabilities(pgp_pubkey_alg_t alg) FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: return pgp_key_flags_t(PGP_KF_SIGN | PGP_KF_CERTIFY | PGP_KF_AUTH); #endif @@ -1252,9 +1254,11 @@ pgp_key_t::is_pqc_alg() const FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: return true; default: return false; diff --git a/src/lib/rnp.cpp b/src/lib/rnp.cpp index 6ca0514fd4..1d9f3a480d 100644 --- a/src/lib/rnp.cpp +++ b/src/lib/rnp.cpp @@ -180,8 +180,9 @@ static const id_str_pair pubkey_alg_map[] = { {PGP_PKA_DILITHIUM5_P384, RNP_ALGNAME_DILITHIUM5_P384}, {PGP_PKA_DILITHIUM3_BP256, RNP_ALGNAME_DILITHIUM3_BP256}, {PGP_PKA_DILITHIUM5_BP384, RNP_ALGNAME_DILITHIUM5_BP384}, - {PGP_PKA_SPHINCSPLUS_SHA2, RNP_ALGNAME_SPHINCSPLUS_SHA2}, - {PGP_PKA_SPHINCSPLUS_SHAKE, RNP_ALGNAME_SPHINCSPLUS_SHAKE}, + {PGP_PKA_SPHINCSPLUS_SHAKE_128f, RNP_ALGNAME_SPHINCSPLUS_SHAKE_128f}, + {PGP_PKA_SPHINCSPLUS_SHAKE_128s, RNP_ALGNAME_SPHINCSPLUS_SHAKE_128s}, + {PGP_PKA_SPHINCSPLUS_SHAKE_256s, RNP_ALGNAME_SPHINCSPLUS_SHAKE_256s}, #endif {0, NULL}}; @@ -225,16 +226,6 @@ static const id_str_pair hash_alg_map[] = {{PGP_HASH_MD5, RNP_ALGNAME_MD5}, {PGP_HASH_SM3, RNP_ALGNAME_SM3}, {0, NULL}}; -#if defined(ENABLE_PQC) -static const id_str_pair sphincsplus_params_map[] = {{sphincsplus_simple_128s, "128s"}, - {sphincsplus_simple_128f, "128f"}, - {sphincsplus_simple_192s, "192s"}, - {sphincsplus_simple_192f, "192f"}, - {sphincsplus_simple_256s, "256s"}, - {sphincsplus_simple_256f, "256f"}, - {0, NULL}}; -#endif - static const id_str_pair s2k_type_map[] = { {PGP_S2KS_SIMPLE, "Simple"}, {PGP_S2KS_SALTED, "Salted"}, @@ -364,8 +355,9 @@ pub_alg_supported(int alg) case PGP_PKA_DILITHIUM5_P384: case PGP_PKA_DILITHIUM3_BP256: case PGP_PKA_DILITHIUM5_BP384: - case PGP_PKA_SPHINCSPLUS_SHA2: - case PGP_PKA_SPHINCSPLUS_SHAKE: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: #endif return true; default: @@ -5067,9 +5059,11 @@ default_key_flags(pgp_pubkey_alg_t alg, bool subkey) FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: return subkey ? PGP_KF_SIGN : pgp_key_flags_t(PGP_KF_SIGN | PGP_KF_CERTIFY); #endif default: @@ -5472,43 +5466,6 @@ try { FFI_GUARD #endif -#if defined(RNP_EXPERIMENTAL_PQC) -rnp_result_t -rnp_op_generate_set_sphincsplus_param(rnp_op_generate_t op, const char *param_cstr) -try { - if (!op || !param_cstr) { - return RNP_ERROR_NULL_POINTER; - } - auto slhdsa = dynamic_cast(&op->keygen.key_params()); - if (!slhdsa) { - return RNP_ERROR_BAD_PARAMETERS; - } - - sphincsplus_parameter_t param; - std::string param_str = param_cstr; - - if (param_str == "128s") { - param = sphincsplus_simple_128s; - } else if (param_str == "128f") { - param = sphincsplus_simple_128f; - } else if (param_str == "192s") { - param = sphincsplus_simple_192s; - } else if (param_str == "192f") { - param = sphincsplus_simple_192f; - } else if (param_str == "256s") { - param = sphincsplus_simple_256s; - } else if (param_str == "256f") { - param = sphincsplus_simple_256f; - } else { - return RNP_ERROR_BAD_PARAMETERS; - } - - slhdsa->set_param(param); - return RNP_SUCCESS; -} -FFI_GUARD -#endif - rnp_result_t rnp_op_generate_execute(rnp_op_generate_t op) try { @@ -7215,27 +7172,6 @@ try { } FFI_GUARD -#if defined(RNP_EXPERIMENTAL_PQC) -rnp_result_t -rnp_key_sphincsplus_get_param(rnp_key_handle_t handle, char **param) -try { - if (!handle || !param) { - return RNP_ERROR_NULL_POINTER; - } - pgp_key_t *key = get_key_prefer_public(handle); - if (key->alg() != PGP_PKA_SPHINCSPLUS_SHA2 && key->alg() != PGP_PKA_SPHINCSPLUS_SHAKE) { - return RNP_ERROR_BAD_PARAMETERS; - } - - auto material = dynamic_cast(key->material()); - if (!material) { - return RNP_ERROR_BAD_STATE; - } - return get_map_value(sphincsplus_params_map, material->pub().param(), param); -} -FFI_GUARD -#endif - rnp_result_t rnp_key_get_bits(rnp_key_handle_t handle, uint32_t *bits) try { @@ -8134,9 +8070,11 @@ add_json_mpis(json_object *jso, pgp_key_t *key, bool secret = false) FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: return RNP_SUCCESS; /* TODO */ #endif default: @@ -8188,9 +8126,11 @@ add_json_sig_mpis(json_object *jso, const pgp_signature_t *sig) FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: return RNP_SUCCESS; /* TODO */ #endif default: @@ -8429,9 +8369,11 @@ key_to_json(json_object *jso, rnp_key_handle_t handle, uint32_t flags) FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: return RNP_SUCCESS; /* TODO */ #endif default: diff --git a/src/librepgp/stream-dump.cpp b/src/librepgp/stream-dump.cpp index ad425c9551..8219d0eda8 100644 --- a/src/librepgp/stream-dump.cpp +++ b/src/librepgp/stream-dump.cpp @@ -159,8 +159,9 @@ static const id_str_pair pubkey_alg_map[] = { {PGP_PKA_DILITHIUM5_P384, "ML-DSA-87 + NIST P-384"}, {PGP_PKA_DILITHIUM3_BP256, "ML-DSA-65 + Brainpool256"}, {PGP_PKA_DILITHIUM5_BP384, "ML-DSA-87 + Brainpool384"}, - {PGP_PKA_SPHINCSPLUS_SHA2, "SLH-DSA-SHA2"}, - {PGP_PKA_SPHINCSPLUS_SHAKE, "SLH-DSA-SHAKE"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_128f, "SLH-DSA-SHAKE-128f"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_128s, "SLH-DSA-SHAKE-128s"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_256s, "SLH-DSA-SHAKE-256s"}, #endif {0x00, NULL}, }; @@ -866,9 +867,11 @@ stream_dump_signature_pkt(rnp_dump_ctx_t *ctx, pgp_signature_t *sig, pgp_dest_t dst_print_vec( dst, "mldsa-ecdsa/eddsa sig", material.dilithium_exdsa.sig, ctx->dump_mpi); break; - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: dst_print_vec(dst, "slhdsa sig", material.sphincsplus.sig, ctx->dump_mpi); break; #endif @@ -1002,9 +1005,11 @@ stream_dump_key_material(rnp_dump_ctx_t & ctx, ctx.dump_mpi); return; } - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: { + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: { auto &sphincs = dynamic_cast(*material); dst_print_vec(dst, "slhdsa encoded pubkey", sphincs.pub().get_encoded(), ctx.dump_mpi); return; @@ -2081,9 +2086,11 @@ stream_dump_signature_pkt_json(rnp_dump_ctx_t * ctx, case PGP_PKA_DILITHIUM5_BP384: /* TODO */ break; - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: /* TODO */ break; #endif @@ -2209,9 +2216,11 @@ stream_dump_key_material_json(rnp_dump_ctx_t & ctx, case PGP_PKA_DILITHIUM5_BP384: /* TODO */ return true; - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: /* TODO */ return true; #endif diff --git a/src/librepgp/stream-sig.cpp b/src/librepgp/stream-sig.cpp index 0a5e53f8dd..191cb2b026 100644 --- a/src/librepgp/stream-sig.cpp +++ b/src/librepgp/stream-sig.cpp @@ -1101,22 +1101,14 @@ pgp_signature_t::parse_material(pgp_signature_material_t &material) const return false; } break; - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: { - uint8_t param; - if (!pkt.get(param)) { - RNP_LOG("failed to parse SLH-DSA signature data"); - return false; - } - auto sig_size = sphincsplus_signature_size((sphincsplus_parameter_t) param); - if (!sig_size) { - RNP_LOG("invalid SLH-DSA param value"); - return false; - } - material.sphincsplus.param = (sphincsplus_parameter_t) param; - material.sphincsplus.sig.resize(sig_size); - if (!pkt.get(material.sphincsplus.sig.data(), sig_size)) { + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: { + material.sphincsplus.sig.resize( + sphincsplus_signature_size(palg)); + if (!pkt.get(material.sphincsplus.sig.data(), material.sphincsplus.sig.size())) { RNP_LOG("failed to parse SLH-DSA signature data"); return false; } @@ -1221,10 +1213,11 @@ pgp_signature_t::write_material(const pgp_signature_material_t &material) case PGP_PKA_DILITHIUM5_BP384: pktbody.add(material.dilithium_exdsa.sig); break; - case PGP_PKA_SPHINCSPLUS_SHA2: + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE: - pktbody.add_byte((uint8_t) material.sphincsplus.param); + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: pktbody.add(material.sphincsplus.sig); break; #endif diff --git a/src/rnp/fficli.cpp b/src/rnp/fficli.cpp index 555d957efb..b563756bc4 100644 --- a/src/rnp/fficli.cpp +++ b/src/rnp/fficli.cpp @@ -1387,15 +1387,6 @@ cli_rnp_print_key_info(FILE *fp, rnp_ffi_t ffi, rnp_key_handle_t key, bool psecr char *alg = NULL; (void) rnp_key_get_alg(key, &alg); fprintf(fp, "%s", cli_rnp_normalize_key_alg(alg)); -#if defined(ENABLE_PQC) - // in case of a SPHINCS+ key, also print the parameter set - char * param; - rnp_result_t res = rnp_key_sphincsplus_get_param(key, ¶m); - if (res == RNP_SUCCESS) { - fprintf(fp, "-%s", param); - rnp_buffer_destroy(param); - } -#endif fprintf(fp, " "); /* key id */ char *keyid = NULL; @@ -1599,14 +1590,6 @@ cli_rnp_generate_key(cli_rnp_t *rnp, const char *username) rnp_op_generate_set_v6_key(genkey); } #endif -#if defined(ENABLE_PQC) - if (cfg.has(CFG_KG_PRIMARY_SPHINCSPLUS_PARAM) && - rnp_op_generate_set_sphincsplus_param( - genkey, cfg.get_cstr(CFG_KG_PRIMARY_SPHINCSPLUS_PARAM))) { - ERR_MSG("Failed to set sphincsplus parameter."); - goto done; - } -#endif fprintf(rnp->userio_out, "Generating a new key...\n"); if (rnp_op_generate_execute(genkey) || rnp_op_generate_get_key(genkey, &primary)) { @@ -1653,14 +1636,6 @@ cli_rnp_generate_key(cli_rnp_t *rnp, const char *username) if (cfg.get_bool(CFG_KG_V6_KEY)) { rnp_op_generate_set_v6_key(genkey); } -#endif -#if defined(ENABLE_PQC) - if (cfg.has(CFG_KG_SUBKEY_SPHINCSPLUS_PARAM) && - rnp_op_generate_set_sphincsplus_param(genkey, - cfg.get_cstr(CFG_KG_SUBKEY_SPHINCSPLUS_PARAM))) { - ERR_MSG("Failed to set sphincsplus parameter."); - goto done; - } #endif if (rnp_op_generate_execute(genkey) || rnp_op_generate_get_key(genkey, &subkey)) { ERR_MSG("Subkey generation failed."); @@ -1704,12 +1679,6 @@ cli_rnp_generate_key(cli_rnp_t *rnp, const char *username) rnp_op_generate_set_v6_key(genkey); } #endif - if (cfg.has(CFG_KG_SUBKEY_2_SPHINCSPLUS_PARAM) && - rnp_op_generate_set_sphincsplus_param( - genkey, cfg.get_cstr(CFG_KG_SUBKEY_2_SPHINCSPLUS_PARAM))) { - ERR_MSG("Failed to set sphincsplus parameter."); - goto done; - } if (rnp_op_generate_execute(genkey) || rnp_op_generate_get_key(genkey, &subkey2)) { ERR_MSG("Subkey generation failed."); goto done; diff --git a/src/rnp/rnpcfg.h b/src/rnp/rnpcfg.h index eda6960efe..f9824db181 100644 --- a/src/rnp/rnpcfg.h +++ b/src/rnp/rnpcfg.h @@ -130,14 +130,6 @@ #define CFG_KG_V6_KEY \ "kg-v6-key" /* represents a boolean property: non-empty string means 'true' */ #endif -#if defined(ENABLE_PQC) -#define CFG_KG_PRIMARY_SPHINCSPLUS_PARAM \ - "kg-primary-sphincsplus-param" /* 128f, 128s, 192f, 192s, 256f, 256s */ -#define CFG_KG_SUBKEY_SPHINCSPLUS_PARAM \ - "kg-subkey-sphincsplus-param" /* 128f, 128s, 192f, 192s, 256f, 256s */ -#define CFG_KG_SUBKEY_2_SPHINCSPLUS_PARAM \ - "kg-subkey-2-sphincsplus-param" /* 128f, 128s, 192f, 192s, 256f, 256s */ -#endif /* rnp CLI config : contains all the system-dependent and specified by the user configuration * options */ diff --git a/src/rnpkeys/tui.cpp b/src/rnpkeys/tui.cpp index 4d5201a6c0..5ee7077d8a 100644 --- a/src/rnpkeys/tui.cpp +++ b/src/rnpkeys/tui.cpp @@ -151,46 +151,6 @@ ask_curve_name(FILE *input_fp) return result; } -#if defined(ENABLE_PQC) -static std::string -ask_sphincsplus_param_name(FILE *input_fp) -{ - std::vector params = {"128f", "128s", "192f", "192s", "256f", "256s"}; - std::vector add_info = {"ML-KEM-768 + X25519", - "ML-KEM-768 + X25519", - "ML-KEM-1024 + ECDH-NIST-P-384", - "ML-KEM-1024 + ECDH-NIST-P-384", - "ML-KEM-1024 + ECDH-NIST-P-384", - "ML-KEM-1024 + ECDH-NIST-P-384"}; - - const size_t pcount = params.size(); - if (!pcount) { - return NULL; - } - bool ok = false; - std::string result = ""; - int attempts = 0; - do { - if (!check_attempts(attempts)) { - return NULL; - } - printf("Please select which SLH-DSA parameter set you want. In parenthesis, the " - "correspondingly chosen encryption subkey is shown:\n"); - for (size_t i = 0; i < pcount; i++) { - printf("\t(%zu) %s (%s)\n", i + 1, params[i].c_str(), add_info[i].c_str()); - } - printf("(default %s)> ", params[0].c_str()); - long val = 0; - ok = rnp_secure_get_long_from_fd(input_fp, val) && (val > 0) && (val <= (long) pcount); - if (ok) { - result = params[val - 1]; - } - } while (!ok); - - return result; -} -#endif - static long ask_rsa_bitlen(FILE *input_fp) { @@ -295,8 +255,9 @@ rnpkeys_ask_generate_params(rnp_cfg &cfg, FILE *input_fp) "\t(28) (ML-DSA-87 + ECDSA-NIST-P-384) + (ML-KEM-1024 + ECDH-NIST-P-384)\n" "\t(29) (ML-DSA-65 + ECDSA-brainpoolP256r1) + (ML-KEM-768 + ECDH-brainpoolP256r1)\n" "\t(30) (ML-DSA-87 + ECDSA-brainpoolP384r1) + (ML-KEM-1024 + ECDH-brainpoolP384r1)\n" - "\t(31) SLH-DSA-SHA2 + MLKEM-ECDH Composite\n" - "\t(32) SLH-DSA-SHAKE + MLKEM-ECDH Composite\n" + "\t(31) SLH-DSA-SHAKE-128f + (ML-KEM-768 + X25519)\n" + "\t(32) SLH-DSA-SHAKE-128s + (ML-KEM-768 + X25519)\n" + "\t(33) SLH-DSA-SHAKE-256s + (ML-KEM-1024 + ECDH-NIST-P-384)\n" #endif #endif "\t(99) SM2\n" @@ -409,36 +370,23 @@ rnpkeys_ask_generate_params(rnp_cfg &cfg, FILE *input_fp) cfg.set_str(CFG_KG_V6_KEY, "true"); break; case 31: { - std::string param = ask_sphincsplus_param_name(input_fp); - if (param == "") { - return false; - } - if (param == "128f" || param == "128s") { - cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER768_X25519); - cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA256); - } else { - cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER1024_P384); - cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA512); - } - cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_SPHINCSPLUS_SHA2); - cfg.set_str(CFG_KG_PRIMARY_SPHINCSPLUS_PARAM, param); + cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_SPHINCSPLUS_SHAKE_128f); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER768_X25519); + cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_256); cfg.set_str(CFG_KG_V6_KEY, "true"); break; } case 32: { - std::string param = ask_sphincsplus_param_name(input_fp); - if (param == "") { - return false; - } - if (param == "128f" || param == "128s") { - cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER768_X25519); - cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_256); - } else { - cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER1024_P384); - cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_512); - } - cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_SPHINCSPLUS_SHAKE); - cfg.set_str(CFG_KG_PRIMARY_SPHINCSPLUS_PARAM, param); + cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_SPHINCSPLUS_SHAKE_128s); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER768_X25519); + cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_256); + cfg.set_str(CFG_KG_V6_KEY, "true"); + break; + } + case 33: { + cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_SPHINCSPLUS_SHAKE_256s); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER1024_P384); + cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_512); cfg.set_str(CFG_KG_V6_KEY, "true"); break; } diff --git a/src/tests/cipher.cpp b/src/tests/cipher.cpp index e84a631741..936daebfe0 100644 --- a/src/tests/cipher.cpp +++ b/src/tests/cipher.cpp @@ -638,41 +638,34 @@ TEST_F(rnp_tests, dilithium_exdsa_signverify_success) TEST_F(rnp_tests, sphincsplus_signverify_success) { - uint8_t message[64]; - pgp_pubkey_alg_t algs[] = {PGP_PKA_SPHINCSPLUS_SHA2, PGP_PKA_SPHINCSPLUS_SHAKE}; - sphincsplus_parameter_t params[] = {sphincsplus_simple_128s, - sphincsplus_simple_128f, - sphincsplus_simple_192s, - sphincsplus_simple_192f, - sphincsplus_simple_256s, - sphincsplus_simple_256f}; + uint8_t message[64]; + pgp_pubkey_alg_t algs[] = {PGP_PKA_SPHINCSPLUS_SHAKE_128f, + PGP_PKA_SPHINCSPLUS_SHAKE_128s, + PGP_PKA_SPHINCSPLUS_SHAKE_256s}; for (size_t i = 0; i < ARRAY_SIZE(algs); i++) { - for (size_t j = 0; j < ARRAY_SIZE(params); j++) { - // Generate test data. Mainly to make valgrind not to complain about uninitialized - // data - global_ctx.rng.get(message, sizeof(message)); - - rnp::KeygenParams keygen(algs[i], global_ctx); - auto &slhdsa = dynamic_cast(keygen.key_params()); - slhdsa.set_param(params[j]); - - pgp_key_pkt_t seckey1; - pgp_key_pkt_t seckey2; - - assert_true(keygen.generate(seckey1, true)); - assert_true(keygen.generate(seckey2, true)); - - auto & key1 = *seckey1.material; - auto & key2 = *seckey2.material; - rnp::secure_vector hash(message, message + sizeof(message)); - pgp_signature_material_t sig; - assert_rnp_success(key1.sign(global_ctx, sig, hash)); - assert_rnp_success(key1.verify(global_ctx, sig, hash)); - - // Fails because of different key used - assert_rnp_failure(key2.verify(global_ctx, sig, hash)); - } + // Generate test data. Mainly to make valgrind not to complain about uninitialized + // data + global_ctx.rng.get(message, sizeof(message)); + + rnp::KeygenParams keygen(algs[i], global_ctx); + + pgp_key_pkt_t seckey1; + pgp_key_pkt_t seckey2; + + assert_true(keygen.generate(seckey1, true)); + assert_true(keygen.generate(seckey2, true)); + + auto &key1 = *seckey1.material; + auto &key2 = *seckey2.material; + + rnp::secure_vector hash(message, message + sizeof(message)); + pgp_signature_material_t sig; + assert_rnp_success(key1.sign(global_ctx, sig, hash)); + assert_rnp_success(key1.verify(global_ctx, sig, hash)); + + // Fails because of different key used + assert_rnp_failure(key2.verify(global_ctx, sig, hash)); } } #endif diff --git a/src/tests/cli_tests.py b/src/tests/cli_tests.py index 0bf79eadc6..0d134c9964 100755 --- a/src/tests/cli_tests.py +++ b/src/tests/cli_tests.py @@ -4652,15 +4652,16 @@ def test_encryption_and_signing_pqc(self): "(28) (ML-DSA-87 + ECDSA-NIST-P-384) + (ML-KEM-1024 + ECDH-NIST-P-384)", "(29) (ML-DSA-65 + ECDSA-brainpoolP256r1) + (ML-KEM-768 + ECDH-brainpoolP256r1)", "(30) (ML-DSA-87 + ECDSA-brainpoolP384r1) + (ML-KEM-1024 + ECDH-brainpoolP384r1)", - "(31) SLH-DSA-SHA2 + MLKEM-ECDH Composite", - "(32) SLH-DSA-SHAKE + MLKEM-ECDH Composite", + "(31) SLH-DSA-SHAKE-128f + (ML-KEM-768 + X25519)", + "(32) SLH-DSA-SHAKE-128s + (ML-KEM-768 + X25519)", + "(33) SLH-DSA-SHAKE-256s + (ML-KEM-1024 + ECDH-NIST-P-384)", ] USERIDS = ['enc-sign25@rnp', 'enc-sign27@rnp', 'enc-sign28@rnp', 'enc-sign29@rnp', 'enc-sign30@rnp','enc-sign32a@rnp','enc-sign32b@rnp','enc-sign32c@rnp','enc-sign24-v4-key@rnp'] # '24' in the below array creates a v4 primary signature key with a v4 pqc subkey without a Features Subpacket. This way we test PQC encryption to a v4 subkey. RNP prefers the PQC subkey in case of a certificate having a PQC and a # non-PQC subkey. - ALGO = [25, 27, 28, 29, 30, 32, 32, 32, 24, ] - ALGO_PARAM = [None, None, None, None, None, 1, 2, 6, None, ] + ALGO = [25, 27, 28, 29, 30, 31, 32, 33, 24, ] + ALGO_PARAM = [None, None, None, None, None, None, None, None, None, ] aead_list = [] passwds = [ ] for x in range(len(ALGO)): passwds.append('testpw' if x % 1 == 0 else '') diff --git a/src/tests/ffi.cpp b/src/tests/ffi.cpp index d84cad8f5d..377a4873f5 100644 --- a/src/tests/ffi.cpp +++ b/src/tests/ffi.cpp @@ -3109,7 +3109,7 @@ TEST_F(rnp_tests, test_ffi_supported_features) crypto_refresh_opt = 2; // X25519 + ED25519 #endif #if defined(ENABLE_PQC) - pqc_opt = 12; // kyber+ecc and dilithium+ecc and sphincs+ variants + pqc_opt = 13; // kyber+ecc and dilithium+ecc and sphincs+ variants #endif assert_true(check_features( RNP_FEATURE_PK_ALG, features, 6 + has_sm2 + pqc_opt + crypto_refresh_opt)); diff --git a/src/tests/pqc.cpp b/src/tests/pqc.cpp index 0b779d4970..1f62b62fed 100644 --- a/src/tests/pqc.cpp +++ b/src/tests/pqc.cpp @@ -68,28 +68,20 @@ TEST_F(rnp_tests, test_dilithium_key_function) TEST_F(rnp_tests, test_sphincsplus_key_function) { - sphincsplus_parameter_t params[] = {sphincsplus_simple_128s, - sphincsplus_simple_128f, - sphincsplus_simple_192s, - sphincsplus_simple_192f, - sphincsplus_simple_256s, - sphincsplus_simple_256f}; - sphincsplus_hash_func_t hash_funcs[] = {sphincsplus_sha256, sphinscplus_shake256}; - - for (sphincsplus_parameter_t param : params) { - for (sphincsplus_hash_func_t hash_func : hash_funcs) { - auto public_and_private_key = - sphincsplus_generate_keypair(&global_ctx.rng, param, hash_func); - - std::array msg{'H', 'e', 'l', 'l', 'o'}; - - pgp_sphincsplus_signature_t sig; - assert_rnp_success(public_and_private_key.second.sign( - &global_ctx.rng, &sig, msg.data(), msg.size())); - - assert_rnp_success( - public_and_private_key.first.verify(&sig, msg.data(), msg.size())); - } + pgp_pubkey_alg_t algs[] = {PGP_PKA_SPHINCSPLUS_SHAKE_128f, + PGP_PKA_SPHINCSPLUS_SHAKE_128s, + PGP_PKA_SPHINCSPLUS_SHAKE_256s}; + + for (pgp_pubkey_alg_t alg : algs) { + auto public_and_private_key = sphincsplus_generate_keypair(&global_ctx.rng, alg); + + std::array msg{'H', 'e', 'l', 'l', 'o'}; + + pgp_sphincsplus_signature_t sig; + assert_rnp_success( + public_and_private_key.second.sign(&global_ctx.rng, &sig, msg.data(), msg.size())); + + assert_rnp_success(public_and_private_key.first.verify(&sig, msg.data(), msg.size())); } } From 00dede5def292f28d4f341cfbbef8ab0670af39a Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Mon, 30 Sep 2024 14:33:14 +0200 Subject: [PATCH 02/15] improve v6 enc test --- src/tests/ffi-enc.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp index 73709dd352..4d870e2890 100644 --- a/src/tests/ffi-enc.cpp +++ b/src/tests/ffi-enc.cpp @@ -992,13 +992,30 @@ TEST_F(rnp_tests, test_ffi_encrypt_pk_with_v6_key) assert_true(import_all_keys(ffi, "data/test_v6_valid_data/transferable_seckey_v6.asc")); + // No other cipher can be used here: the only 128-bit block ciphers in OpenPGP aside from + // AES are Camellia and TwoFish. std::vector ciphers = {"AES128", "AES192", "AES256"}; +#ifdef ENABLE_TWOFISH + ciphers.push_back("TwoFish"); +#endif +#if (defined BOTAN_HAS_CAMELLIA || !defined CRYPTO_BACKEND_BOTAN3) + // ENABLE_CAMELLIA does not exist, yet BOTAN might not support this algorithm. + std::vector camellia({"Camellia128", "Camellia192", "Camellia256"}); + ciphers.insert(ciphers.end(), camellia.begin(), camellia.end()); +#endif std::vector aead_modes = {"None", "EAX", "OCB"}; std::vector enable_pkeskv6_modes = {true, false}; for (auto enable_pkeskv6 : enable_pkeskv6_modes) for (auto aead : aead_modes) for (auto cipher : ciphers) { + bool expect_success = true; + if (!enable_pkeskv6 && (cipher == "TwoFish" || cipher.find("Camellia") == 0)) { + // This combination is not supported, since the algorithm ID is fixed to + // AES for v3 PKESK for the public key algorithm featured by the key used + // here. + expect_success = false; + } // write out some data FILE *fp = fopen("plaintext", "wb"); assert_non_null(fp); @@ -1036,7 +1053,12 @@ TEST_F(rnp_tests, test_ffi_encrypt_pk_with_v6_key) assert_rnp_success(rnp_op_encrypt_set_cipher(op, cipher.c_str())); // execute the operation - assert_rnp_success(rnp_op_encrypt_execute(op)); + if (expect_success) { + assert_rnp_success(rnp_op_encrypt_execute(op)); + } else { + assert_rnp_failure(rnp_op_encrypt_execute(op)); + continue; + } // make sure the output file was created assert_true(rnp_file_exists("encrypted")); @@ -1049,8 +1071,6 @@ TEST_F(rnp_tests, test_ffi_encrypt_pk_with_v6_key) assert_rnp_success(rnp_op_encrypt_destroy(op)); op = NULL; - /* decrypt */ - // decrypt assert_rnp_success(rnp_input_from_path(&input, "encrypted")); assert_non_null(input); From 44a55cd9f07ae9cc75dd93992051579a11884eea Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Tue, 1 Oct 2024 10:19:40 +0200 Subject: [PATCH 03/15] fix key generation PQC hash requirements ... for rnp_generate_key_ex add roundtrip test for PQC certs clang-format --- src/lib/crypto/sphincsplus.h | 19 +++--- src/lib/generate-key.cpp | 122 +++++++++++++++++++++++++++-------- src/lib/key_material.cpp | 2 +- src/librepgp/stream-sig.cpp | 3 +- src/tests/ffi-enc.cpp | 63 ++++++++++++++++++ 5 files changed, 170 insertions(+), 39 deletions(-) diff --git a/src/lib/crypto/sphincsplus.h b/src/lib/crypto/sphincsplus.h index ff9204164d..81bbfad996 100644 --- a/src/lib/crypto/sphincsplus.h +++ b/src/lib/crypto/sphincsplus.h @@ -39,12 +39,12 @@ struct pgp_sphincsplus_key_t; struct pgp_sphincsplus_signature_t; typedef struct pgp_sphincsplus_signature_t { - std::vector sig; + std::vector sig; } pgp_sphincsplus_signature_t; class pgp_sphincsplus_private_key_t { public: - pgp_sphincsplus_private_key_t(const uint8_t *key_encoded, + pgp_sphincsplus_private_key_t(const uint8_t * key_encoded, size_t key_encoded_len, pgp_pubkey_alg_t alg); pgp_sphincsplus_private_key_t(std::vector const &key_encoded, @@ -122,17 +122,17 @@ class pgp_sphincsplus_public_key_t { private: Botan::SphincsPlus_PublicKey botan_key() const; - std::vector key_encoded_; - pgp_pubkey_alg_t pk_alg_; - bool is_initialized_ = false; + std::vector key_encoded_; + pgp_pubkey_alg_t pk_alg_; + bool is_initialized_ = false; }; std::pair sphincsplus_generate_keypair(rnp::RNG *rng, pgp_pubkey_alg_t alg); -rnp_result_t pgp_sphincsplus_generate(rnp::RNG * rng, - pgp_sphincsplus_key_t * material, - pgp_pubkey_alg_t alg); +rnp_result_t pgp_sphincsplus_generate(rnp::RNG * rng, + pgp_sphincsplus_key_t *material, + pgp_pubkey_alg_t alg); rnp_result_t sphincsplus_validate_key(rnp::RNG * rng, const pgp_sphincsplus_key_t *key, @@ -147,8 +147,7 @@ size_t sphincsplus_privkey_size(pgp_pubkey_alg_t alg); size_t sphincsplus_pubkey_size(pgp_pubkey_alg_t alg); size_t sphincsplus_signature_size(pgp_pubkey_alg_t alg); -bool sphincsplus_hash_allowed(pgp_pubkey_alg_t pk_alg, - pgp_hash_alg_t hash_alg); +bool sphincsplus_hash_allowed(pgp_pubkey_alg_t pk_alg, pgp_hash_alg_t hash_alg); pgp_hash_alg_t sphincsplus_default_hash_alg(pgp_pubkey_alg_t pk_alg); diff --git a/src/lib/generate-key.cpp b/src/lib/generate-key.cpp index 5255bed7b8..79c9d15d77 100644 --- a/src/lib/generate-key.cpp +++ b/src/lib/generate-key.cpp @@ -162,6 +162,34 @@ pk_alg_default_flags(pgp_pubkey_alg_t alg) static void adjust_hash_alg(rnp_keygen_crypto_params_t &crypto) { +#if defined(ENABLE_PQC) + if (!crypto.hash_alg) { + // Set usable default hash for PQC key if not explicitly set before. + // Note that for PQC keys, some constraints for the used hash algorithm exist. + switch (crypto.key_alg) { + case PGP_PKA_DILITHIUM3_ED25519: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM3_P256: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM3_BP256: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + crypto.hash_alg = PGP_HASH_SHA3_256; + break; + case PGP_PKA_DILITHIUM5_BP384: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_P384: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: + crypto.hash_alg = PGP_HASH_SHA3_512; + break; + default: + break; + } + } +#endif if (!crypto.hash_alg) { crypto.hash_alg = (pgp_hash_alg_t) DEFAULT_HASH_ALGS[0]; } @@ -179,6 +207,41 @@ adjust_hash_alg(rnp_keygen_crypto_params_t &crypto) } } +#if defined(ENABLE_PQC) +static bool +pgp_check_key_hash_requirements(const rnp_keygen_crypto_params_t &crypto) +{ + switch (crypto.key_alg) { + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: + if (!sphincsplus_hash_allowed(crypto.key_alg, crypto.hash_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 (!dilithium_hash_allowed(crypto.hash_alg)) { + return false; + } + break; + default: + break; + } + return true; +} +#endif + static void keygen_merge_crypto_defaults(rnp_keygen_crypto_params_t &crypto) { @@ -255,6 +318,13 @@ validate_keygen_primary(const rnp_keygen_primary_desc_t &desc) RNP_LOG("usage not permitted for pk algorithm"); return false; } +#if defined(ENABLE_PQC) + // check hash requirements + if (!pgp_check_key_hash_requirements(desc.crypto)) { + RNP_LOG("invalid hash algorithm for the chosen key"); + return false; + } +#endif // require a userid if (!desc.cert.userid[0]) { RNP_LOG("userid is required for primary key"); @@ -377,25 +447,11 @@ keygen_primary_merge_defaults(rnp_keygen_primary_desc_t &desc) getenv_logname()); desc.cert.userid = uid; } -} -#if defined(ENABLE_PQC) -static bool -pgp_check_key_hash_requirements(rnp_keygen_crypto_params_t &crypto) -{ - switch (crypto.key_alg) { - case PGP_PKA_SPHINCSPLUS_SHAKE_128f: - FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE_128s: - FALLTHROUGH_STATEMENT; - case PGP_PKA_SPHINCSPLUS_SHAKE_256s: - if (!sphincsplus_hash_allowed(crypto.key_alg, crypto.hash_alg)) { - return false; - } - break; +#if defined(ENABLE_CRYPTO_REFRESH) && defined(ENABLE_PQC) + switch (desc.crypto.key_alg) { 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: @@ -403,16 +459,19 @@ pgp_check_key_hash_requirements(rnp_keygen_crypto_params_t &crypto) case PGP_PKA_DILITHIUM3_BP256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: - if (!dilithium_hash_allowed(crypto.hash_alg)) { - return false; - } - break; + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: + // set the correct key version for PQC primary keys + desc.pgp_version = PGP_V6; default: break; } - return true; -} #endif +} bool pgp_generate_primary_key(rnp_keygen_primary_desc_t &desc, @@ -503,6 +562,13 @@ validate_keygen_subkey(rnp_keygen_subkey_desc_t &desc) RNP_LOG("usage not permitted for pk algorithm"); return false; } +#if defined(ENABLE_PQC) + // check hash requirements + if (!pgp_check_key_hash_requirements(desc.crypto)) { + RNP_LOG("invalid hash algorithm for the chosen key"); + return false; + } +#endif return true; } @@ -540,6 +606,10 @@ pgp_generate_subkey(rnp_keygen_subkey_desc_t & desc, // merge some defaults in, if requested if (merge_defaults) { keygen_subkey_merge_defaults(desc); +#if defined(ENABLE_CRYPTO_REFRESH) + // by default make subkey version match primary key version + desc.pgp_version = primary_sec.version(); +#endif } // now validate the keygen fields @@ -547,10 +617,10 @@ pgp_generate_subkey(rnp_keygen_subkey_desc_t & desc, return false; } -#if defined(ENABLE_PQC) - // check hash requirements - if (!pgp_check_key_hash_requirements(desc.crypto)) { - RNP_LOG("invalid hash algorithm for the chosen key"); +#if defined(ENABLE_CRYPTO_REFRESH) + // assure that key versions match between subkeys + if (desc.pgp_version != primary_sec.version()) { + RNP_LOG("primary and subkey versions do not match"); return false; } #endif diff --git a/src/lib/key_material.cpp b/src/lib/key_material.cpp index 1a00baf882..fa248ca2b4 100644 --- a/src/lib/key_material.cpp +++ b/src/lib/key_material.cpp @@ -1951,7 +1951,7 @@ SlhdsaKeyMaterial::parse(pgp_packet_body_t &pkt) noexcept bool SlhdsaKeyMaterial::parse_secret(pgp_packet_body_t &pkt) noexcept { - std::vector buf(sphincsplus_privkey_size(alg())); + std::vector buf(sphincsplus_privkey_size(alg())); if (!pkt.get(buf.data(), buf.size())) { RNP_LOG("failed to parse SLH-DSA secret key data"); return false; diff --git a/src/librepgp/stream-sig.cpp b/src/librepgp/stream-sig.cpp index 191cb2b026..4f8beaed31 100644 --- a/src/librepgp/stream-sig.cpp +++ b/src/librepgp/stream-sig.cpp @@ -1106,8 +1106,7 @@ pgp_signature_t::parse_material(pgp_signature_material_t &material) const case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; case PGP_PKA_SPHINCSPLUS_SHAKE_256s: { - material.sphincsplus.sig.resize( - sphincsplus_signature_size(palg)); + material.sphincsplus.sig.resize(sphincsplus_signature_size(palg)); if (!pkt.get(material.sphincsplus.sig.data(), material.sphincsplus.sig.size())) { RNP_LOG("failed to parse SLH-DSA signature data"); return false; diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp index 4d870e2890..6b4e07134f 100644 --- a/src/tests/ffi-enc.cpp +++ b/src/tests/ffi-enc.cpp @@ -829,6 +829,69 @@ TEST_F(rnp_tests, test_ffi_decrypt_pk_unlocked) } #if defined(ENABLE_CRYPTO_REFRESH) +#if defined(ENABLE_PQC) +/* generate keys and make sure they are usable */ +TEST_F(rnp_tests, test_ffi_pqc_gen_enc_sign) +{ + std::vector> primary_sub = { + {"ML-DSA-65+ED25519", "ML-KEM-768+X25519"}, + {"ML-DSA-65+ECDSA-P256", "ML-KEM-768+ECDH-P256"}, + {"ML-DSA-87+ECDSA-P384", "ML-KEM-1024+ECDH-P384"}, + {"ML-DSA-65+ECDSA-BP256", "ML-KEM-768+ECDH-BP256"}, + {"ML-DSA-87+ECDSA-BP384", "ML-KEM-1024+ECDH-BP384"}, + {"SLH-DSA-SHAKE-128f", "ML-KEM-768+X25519"}, + {"SLH-DSA-SHAKE-128s", "ML-KEM-768+X25519"}, + {"SLH-DSA-SHAKE-256s", "ML-KEM-768+X25519"}}; + for (auto pk_algs : primary_sub) { + rnp_ffi_t ffi = NULL; + rnp_key_handle_t key = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_encrypt_t op = NULL; + const char * plaintext = "data1"; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_generate_key_ex(ffi, + pk_algs.first.c_str(), + pk_algs.second.c_str(), + 0, + 0, + NULL, + NULL, + "Test UID", + NULL, + &key)); + // encrypt+sign + str_to_file("plaintext", plaintext); + assert_rnp_success(rnp_input_from_path(&input, "plaintext")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "encrypted")); + assert_non_null(output); + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "AES256")); + assert_rnp_success(rnp_op_encrypt_add_signature(op, key, NULL)); + assert_rnp_success(rnp_op_encrypt_execute(op)); + assert_true(rnp_file_exists("encrypted")); + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + /* decrypt */ + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + // cleanup + assert_rnp_success(rnp_op_encrypt_destroy(op)); + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); + } +} +#endif + TEST_F(rnp_tests, test_ffi_decrypt_v6_pkesk_test_vector) { rnp_ffi_t ffi = NULL; From 22cfa7632c63e0b9a2235dd2b22b6f330af964d2 Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Tue, 1 Oct 2024 16:21:08 +0200 Subject: [PATCH 04/15] Update PQC to draft-05 require Botan 3.6.0 for PQC switch to final NIST PQC standards update KMAC Key Combiner --- CMakeLists.txt | 5 +- src/lib/CMakeLists.txt | 17 +++-- src/lib/crypto/dilithium.cpp | 4 +- src/lib/crypto/dilithium_common.cpp | 17 ++--- src/lib/crypto/dilithium_common.h | 2 +- src/lib/crypto/dilithium_exdsa_composite.cpp | 6 +- src/lib/crypto/kmac.cpp | 77 +++++++------------- src/lib/crypto/kmac.hpp | 40 ++++------ src/lib/crypto/kmac_botan.cpp | 16 ++-- src/lib/crypto/kmac_botan.hpp | 2 + src/lib/crypto/kyber.cpp | 28 ++----- src/lib/crypto/kyber_common.cpp | 27 +------ src/lib/crypto/kyber_common.h | 3 +- src/lib/crypto/kyber_ecdh_composite.cpp | 29 ++++++-- src/lib/crypto/kyber_ecdh_composite.h | 4 +- src/lib/key_material.cpp | 3 +- src/tests/ffi-enc.cpp | 33 ++------- src/tests/ffi-key.cpp | 3 - 18 files changed, 121 insertions(+), 195 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index db78e5fd82..83b011366e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,10 +82,7 @@ tristate_feature_auto(ENABLE_RIPEMD160 "Enable RIPEMD-160 hash support.") option(ENABLE_CRYPTO_REFRESH "Enable crypto-refresh support (v6)") option(ENABLE_PQC "Enable PQC support") -# Note: The following two flags are only temporary and will be removed once POC is in a stable state -if (DEFINED ENABLE_PQC_MLKEM_IPD) - add_definitions(-DENABLE_PQC_MLKEM_IPD) -endif() +# Note: The following flag is only temporary and will be removed once POC is in a stable state if (DEFINED ENABLE_PQC_DBG_LOG) add_definitions(-DENABLE_PQC_DBG_LOG) endif() diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index b7f6022c65..b6c7496ae2 100755 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -31,12 +31,17 @@ find_package(ZLIB REQUIRED) # required packages find_package(JSON-C 0.11 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) +if(ENABLE_PQC) + find_package(Botan 3.6.0 REQUIRED) + set(CRYPTO_BACKEND_BOTAN3 1) +else() + 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() endif() if (CRYPTO_BACKEND_OPENSSL) diff --git a/src/lib/crypto/dilithium.cpp b/src/lib/crypto/dilithium.cpp index bd7f112bd4..1561b24ef6 100644 --- a/src/lib/crypto/dilithium.cpp +++ b/src/lib/crypto/dilithium.cpp @@ -32,9 +32,9 @@ namespace { Botan::DilithiumMode rnp_dilithium_param_to_botan_dimension(dilithium_parameter_e mode) { - Botan::DilithiumMode result = Botan::DilithiumMode::Dilithium8x7; + Botan::DilithiumMode result = Botan::DilithiumMode::ML_DSA_8x7; if (mode == dilithium_parameter_e::dilithium_L3) { - result = Botan::DilithiumMode::Dilithium6x5; + result = Botan::DilithiumMode::ML_DSA_6x5; } return result; } diff --git a/src/lib/crypto/dilithium_common.cpp b/src/lib/crypto/dilithium_common.cpp index d28c7b678f..ff28bd4c85 100644 --- a/src/lib/crypto/dilithium_common.cpp +++ b/src/lib/crypto/dilithium_common.cpp @@ -58,17 +58,10 @@ pgp_dilithium_private_key_t::pgp_dilithium_private_key_t( } size_t -dilithium_privkey_size(dilithium_parameter_e parameter) +dilithium_privkey_size() { - switch (parameter) { - case dilithium_L3: - return 4000; - case dilithium_L5: - return 4864; - default: - RNP_LOG("invalid parameter given"); - throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); - } + /* seed format */ + return 32; } size_t @@ -90,9 +83,9 @@ dilithium_signature_size(dilithium_parameter_e parameter) { switch (parameter) { case dilithium_L3: - return 3293; + return 3309; case dilithium_L5: - return 4595; + return 4627; default: RNP_LOG("invalid parameter given"); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); diff --git a/src/lib/crypto/dilithium_common.h b/src/lib/crypto/dilithium_common.h index 40b9a961eb..e7e8378e10 100644 --- a/src/lib/crypto/dilithium_common.h +++ b/src/lib/crypto/dilithium_common.h @@ -29,7 +29,7 @@ #include "dilithium.h" -size_t dilithium_privkey_size(dilithium_parameter_e parameter); +size_t dilithium_privkey_size(); size_t dilithium_pubkey_size(dilithium_parameter_e parameter); size_t dilithium_signature_size(dilithium_parameter_e parameter); diff --git a/src/lib/crypto/dilithium_exdsa_composite.cpp b/src/lib/crypto/dilithium_exdsa_composite.cpp index 8de71c0067..f097950e74 100644 --- a/src/lib/crypto/dilithium_exdsa_composite.cpp +++ b/src/lib/crypto/dilithium_exdsa_composite.cpp @@ -257,8 +257,7 @@ pgp_dilithium_exdsa_composite_private_key_t::pgp_dilithium_exdsa_composite_priva : pk_alg_(pk_alg) { if (exdsa_curve_privkey_size(pk_alg_to_curve_id(pk_alg)) != exdsa_key_encoded.size() || - dilithium_privkey_size(pk_alg_to_dilithium_id(pk_alg)) != - dilithium_key_encoded.size()) { + dilithium_privkey_size() != dilithium_key_encoded.size()) { RNP_LOG("exdsa or mldsa key length mismatch"); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); } @@ -273,9 +272,8 @@ pgp_dilithium_exdsa_composite_private_key_t::pgp_dilithium_exdsa_composite_priva size_t pgp_dilithium_exdsa_composite_private_key_t::encoded_size(pgp_pubkey_alg_t pk_alg) { - dilithium_parameter_e dilithium_param = pk_alg_to_dilithium_id(pk_alg); pgp_curve_t curve = pk_alg_to_curve_id(pk_alg); - return exdsa_curve_privkey_size(curve) + dilithium_privkey_size(dilithium_param); + return exdsa_curve_privkey_size(curve) + dilithium_privkey_size(); } void diff --git a/src/lib/crypto/kmac.cpp b/src/lib/crypto/kmac.cpp index 207e5981a5..4a8470ab37 100644 --- a/src/lib/crypto/kmac.cpp +++ b/src/lib/crypto/kmac.cpp @@ -59,66 +59,45 @@ KMAC256::domSeparation() const } std::vector -KMAC256::customizationString() const -{ - return customizationString_; -} - -std::vector -KMAC256::counter() const +KMAC256::Input_X(const std::vector &ecc_ciphertext, + const std::vector &kyber_ciphertext, + const std::vector &ecc_pub, + const std::vector &kyber_pub, + pgp_pubkey_alg_t alg_id) { - return counter_; -} + std::vector res; -/* - // Input: - // algID - the algorithm ID encoded as octet - - fixedInfo = algID -*/ -std::vector -KMAC256::fixedInfo(pgp_pubkey_alg_t alg_id) -{ - std::vector result; - result.push_back(static_cast(alg_id)); - return result; +#if defined(ENABLE_PQC_DBG_LOG) + RNP_LOG_NO_POS_INFO("KMAC256 Key_K: "); + RNP_LOG_U8VEC(" - eccCipherText: %s", ecc_ciphertext); + RNP_LOG_U8VEC(" - mlkemCipherText: %s", kyber_ciphertext); + RNP_LOG_U8VEC(" - ecdhPublicKey: %s", ecc_pub); + RNP_LOG_U8VEC(" - mlkemPublicKey: %s", kyber_pub); + RNP_LOG(" - algId : %d", alg_id); +#endif + res.insert(res.end(), ecc_ciphertext.begin(), ecc_ciphertext.end()); + res.insert(res.end(), ecc_pub.begin(), ecc_pub.end()); + res.insert(res.end(), kyber_ciphertext.begin(), kyber_ciphertext.end()); + res.insert(res.end(), kyber_pub.begin(), kyber_pub.end()); + res.push_back(static_cast(alg_id)); + return res; } std::vector -KMAC256::encData(const std::vector &ecc_key_share, - const std::vector &ecc_ciphertext, - const std::vector &kyber_key_share, - const std::vector &kyber_ciphertext, - pgp_pubkey_alg_t alg_id) +KMAC256::Key_K(const std::vector &ecc_key_share, + const std::vector &kyber_key_share) { - std::vector enc_data; - std::vector counter_vec = counter(); - std::vector fixedInfo_vec = fixedInfo(alg_id); - - /* draft-wussler-openpgp-pqc-02: + std::vector res; - eccKemData = eccKeyShare || eccCipherText - kyberKemData = kyberKeyShare || kyberCipherText - encData = counter || eccKemData || kyberKemData || fixedInfo - */ #if defined(ENABLE_PQC_DBG_LOG) - RNP_LOG_NO_POS_INFO("KMAC256 encData: "); - RNP_LOG_U8VEC(" - counter: %s", counter_vec); + RNP_LOG_NO_POS_INFO("KMAC256 Key_K: "); RNP_LOG_U8VEC(" - eccKeyShare: %s", ecc_key_share); - RNP_LOG_U8VEC(" - eccCipherText: %s", ecc_ciphertext); - RNP_LOG_U8VEC(" - kyberKeyShare: %s", kyber_key_share); - RNP_LOG_U8VEC(" - kyberCipherText: %s", kyber_ciphertext); - RNP_LOG_U8VEC(" - fixedInfo: %s", fixedInfo_vec); + RNP_LOG_U8VEC(" - mlkemKeyShare: %s", kyber_key_share); #endif - enc_data.insert(enc_data.end(), counter_vec.begin(), counter_vec.end()); - enc_data.insert(enc_data.end(), ecc_key_share.begin(), ecc_key_share.end()); - enc_data.insert(enc_data.end(), ecc_ciphertext.begin(), ecc_ciphertext.end()); - enc_data.insert(enc_data.end(), kyber_key_share.begin(), kyber_key_share.end()); - enc_data.insert(enc_data.end(), kyber_ciphertext.begin(), kyber_ciphertext.end()); - enc_data.insert(enc_data.end(), fixedInfo_vec.begin(), fixedInfo_vec.end()); - - return enc_data; + res.insert(res.end(), ecc_key_share.begin(), ecc_key_share.end()); + res.insert(res.end(), kyber_key_share.begin(), kyber_key_share.end()); + return res; } KMAC256::~KMAC256() diff --git a/src/lib/crypto/kmac.hpp b/src/lib/crypto/kmac.hpp index 4daa6a52b7..2b6d065986 100644 --- a/src/lib/crypto/kmac.hpp +++ b/src/lib/crypto/kmac.hpp @@ -35,38 +35,28 @@ namespace rnp { class KMAC256 { /* KDF for PQC key combiner according to - * https://datatracker.ietf.org/doc/html/draft-wussler-openpgp-pqc-02 */ + * https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-pqc-05 */ protected: - /* The value of domSeparation is the UTF-8 encoding of the string - "OpenPGPCompositeKeyDerivationFunction" and MUST be the following octet sequence: - - domSeparation := 4F 70 65 6E 50 47 50 43 6F 6D 70 6F 73 69 74 65 - 4B 65 79 44 65 72 69 76 61 74 69 6F 6E 46 75 6E - 63 74 69 6F 6E + /* + // domSep – the UTF-8 encoding of the string "OpenPGPCompositeKDFv1" + // + // domSep given in hexadecimal encoding := 4F 70 65 6E 50 47 50 43 6F 6D 70 + // 6F 73 69 74 65 4B 44 46 76 31 */ - const std::vector domSeparation_ = std::vector( - {0x4F, 0x70, 0x65, 0x6E, 0x50, 0x47, 0x50, 0x43, 0x6F, 0x6D, 0x70, 0x6F, 0x73, - 0x69, 0x74, 0x65, 0x4B, 0x65, 0x79, 0x44, 0x65, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x69, 0x6F, 0x6E, 0x46, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E}); - - /* customizationString := 4B 44 46 */ - const std::vector customizationString_ = std::vector({0x4B, 0x44, 0x46}); - - /* counter - a 4 byte counter set to the value 1 */ - const std::vector counter_ = std::vector({0x00, 0x00, 0x00, 0x01}); + const std::vector domSeparation_ = + std::vector({0x4F, 0x70, 0x65, 0x6E, 0x50, 0x47, 0x50, 0x43, 0x6F, 0x6D, 0x70, + 0x6F, 0x73, 0x69, 0x74, 0x65, 0x4B, 0x44, 0x46, 0x76, 0x31}); std::vector domSeparation() const; - std::vector customizationString() const; - std::vector counter() const; - std::vector fixedInfo(pgp_pubkey_alg_t alg_id); - std::vector encData(const std::vector &ecc_key_share, - const std::vector &ecc_ciphertext, - const std::vector &kyber_key_share, + std::vector Input_X(const std::vector &ecc_ciphertext, const std::vector &kyber_ciphertext, + const std::vector &ecc_pub, + const std::vector &kyber_pub, pgp_pubkey_alg_t alg_id); - + std::vector Key_K(const std::vector &ecc_key_share, + const std::vector &kyber_key_share); KMAC256(){}; public: @@ -75,8 +65,10 @@ class KMAC256 { /* KMAC interface for OpenPGP PQC composite algorithms */ virtual void compute(const std::vector &ecc_key_share, const std::vector &ecc_key_ciphertext, + const std::vector &ecc_pub, const std::vector &kyber_key_share, const std::vector &kyber_ciphertext, + const std::vector &kyber_pub, const pgp_pubkey_alg_t alg_id, std::vector & out) = 0; diff --git a/src/lib/crypto/kmac_botan.cpp b/src/lib/crypto/kmac_botan.cpp index ddee778e25..e4baefe185 100644 --- a/src/lib/crypto/kmac_botan.cpp +++ b/src/lib/crypto/kmac_botan.cpp @@ -47,28 +47,28 @@ KMAC256_Botan::create() void KMAC256_Botan::compute(const std::vector &ecc_key_share, const std::vector &ecc_ciphertext, + const std::vector &ecc_pub, const std::vector &kyber_key_share, const std::vector &kyber_ciphertext, + const std::vector &kyber_pub, const pgp_pubkey_alg_t alg_id, std::vector & out) { auto kmac = Botan::MessageAuthenticationCode::create_or_throw("KMAC-256(256)"); /* the mapping between the KEM Combiner and the MAC interface is: - * key <> domSeparation - * nonce <> customizationString - * message <> encData + * key <> Key_K (key shares) + * nonce <> domSeparation + * message <> Input_X (ciphertexts, pubkeys, alg_id) */ #if defined(ENABLE_PQC_DBG_LOG) RNP_LOG_U8VEC("KMAC256 domSeparation: %s", domSeparation()); - RNP_LOG_U8VEC("KMAC256 customizationString: %s", customizationString()); #endif - kmac->set_key(domSeparation()); - kmac->start(customizationString()); // set nonce - kmac->update( - encData(ecc_key_share, ecc_ciphertext, kyber_key_share, kyber_ciphertext, alg_id)); + kmac->set_key(Key_K(ecc_key_share, kyber_key_share)); + kmac->start(domSeparation()); // set nonce + kmac->update(Input_X(ecc_ciphertext, kyber_ciphertext, ecc_pub, kyber_pub, alg_id)); out = kmac->final_stdvec(); #if defined(ENABLE_PQC_DBG_LOG) diff --git a/src/lib/crypto/kmac_botan.hpp b/src/lib/crypto/kmac_botan.hpp index fb1711a805..b2a9905161 100644 --- a/src/lib/crypto/kmac_botan.hpp +++ b/src/lib/crypto/kmac_botan.hpp @@ -43,8 +43,10 @@ class KMAC256_Botan : public KMAC256 { void compute(const std::vector &ecc_key_share, const std::vector &ecc_ciphertext, + const std::vector &ecc_pub, const std::vector &kyber_key_share, const std::vector &kyber_ciphertext, + const std::vector &kyber_pub, const pgp_pubkey_alg_t alg_id, std::vector & out) override; }; diff --git a/src/lib/crypto/kyber.cpp b/src/lib/crypto/kyber.cpp index eaf78e2c64..a68420b8f1 100644 --- a/src/lib/crypto/kyber.cpp +++ b/src/lib/crypto/kyber.cpp @@ -33,27 +33,17 @@ namespace { Botan::KyberMode rnp_kyber_param_to_botan_kyber_mode(kyber_parameter_e mode) { -#if defined(BOTAN_HAS_ML_KEM_INITIAL_PUBLIC_DRAFT) && defined(ENABLE_PQC_MLKEM_IPD) - Botan::KyberMode result = Botan::KyberMode::ML_KEM_1024_ipd; + Botan::KyberMode result = Botan::KyberMode::ML_KEM_1024; if (mode == kyber_768) { - result = Botan::KyberMode::ML_KEM_768_ipd; + result = Botan::KyberMode::ML_KEM_768; } -#else - Botan::KyberMode result = Botan::KyberMode::Kyber1024; - if (mode == kyber_768) { - result = Botan::KyberMode::Kyber768; - } -#endif return result; } uint32_t -key_share_size_from_kyber_param(kyber_parameter_e param) +kyber_key_share_size() { - if (param == kyber_768) { - return 24; - } - return 32; // kyber_1024 + return 32; } } // namespace @@ -63,6 +53,7 @@ kyber_generate_keypair(rnp::RNG *rng, kyber_parameter_e kyber_param) Botan::Kyber_PrivateKey kyber_priv(*rng->obj(), rnp_kyber_param_to_botan_kyber_mode(kyber_param)); + /* returns the two 32-byte values d and z of ML-KEM.KeyGen() */ Botan::secure_vector encoded_private_key = kyber_priv.private_key_bits(); std::unique_ptr kyber_pub = kyber_priv.public_key(); @@ -98,10 +89,7 @@ pgp_kyber_public_key_t::encapsulate(rnp::RNG *rng) const Botan::secure_vector encap_key; // this has to go over the wire Botan::secure_vector data_encryption_key; // this is the key used for // encryption of the payload data - kem_enc.encrypt(encap_key, - data_encryption_key, - *rng->obj(), - key_share_size_from_kyber_param(kyber_mode_)); + kem_enc.encrypt(encap_key, data_encryption_key, *rng->obj(), kyber_key_share_size()); kyber_encap_result_t result; result.ciphertext.insert( result.ciphertext.end(), encap_key.data(), encap_key.data() + encap_key.size()); @@ -119,8 +107,8 @@ pgp_kyber_private_key_t::decapsulate(rnp::RNG * rng, assert(is_initialized_); auto decoded_kyber_priv = botan_key(); Botan::PK_KEM_Decryptor kem_dec(decoded_kyber_priv, *rng->obj(), "Raw", "base"); - Botan::secure_vector dec_shared_key = kem_dec.decrypt( - ciphertext, ciphertext_len, key_share_size_from_kyber_param(kyber_mode_)); + Botan::secure_vector dec_shared_key = + kem_dec.decrypt(ciphertext, ciphertext_len, kyber_key_share_size()); return std::vector(dec_shared_key.data(), dec_shared_key.data() + dec_shared_key.size()); } diff --git a/src/lib/crypto/kyber_common.cpp b/src/lib/crypto/kyber_common.cpp index 0d70a157da..7ea6900b58 100644 --- a/src/lib/crypto/kyber_common.cpp +++ b/src/lib/crypto/kyber_common.cpp @@ -29,17 +29,10 @@ #include "logging.h" size_t -kyber_privkey_size(kyber_parameter_e parameter) +kyber_privkey_size() { - switch (parameter) { - case kyber_768: - return 2400; - case kyber_1024: - return 3168; - default: - RNP_LOG("invalid parameter given"); - throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); - } + /* seed format */ + return 64; } size_t @@ -56,20 +49,6 @@ kyber_pubkey_size(kyber_parameter_e parameter) } } -size_t -kyber_keyshare_size(kyber_parameter_e parameter) -{ - switch (parameter) { - case kyber_768: - return 24; - case kyber_1024: - return 32; - default: - RNP_LOG("invalid parameter given"); - throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); - } -} - size_t kyber_ciphertext_size(kyber_parameter_e parameter) { diff --git a/src/lib/crypto/kyber_common.h b/src/lib/crypto/kyber_common.h index 4b7978ef11..05ad08218f 100644 --- a/src/lib/crypto/kyber_common.h +++ b/src/lib/crypto/kyber_common.h @@ -29,9 +29,8 @@ #include "kyber.h" -size_t kyber_privkey_size(kyber_parameter_e parameter); +size_t kyber_privkey_size(); size_t kyber_pubkey_size(kyber_parameter_e parameter); -size_t kyber_keyshare_size(kyber_parameter_e parameter); size_t kyber_ciphertext_size(kyber_parameter_e parameter); #endif diff --git a/src/lib/crypto/kyber_ecdh_composite.cpp b/src/lib/crypto/kyber_ecdh_composite.cpp index 647f862252..fe170f6441 100644 --- a/src/lib/crypto/kyber_ecdh_composite.cpp +++ b/src/lib/crypto/kyber_ecdh_composite.cpp @@ -228,7 +228,7 @@ pgp_kyber_ecdh_composite_private_key_t::pgp_kyber_ecdh_composite_private_key_t( : pk_alg_(pk_alg) { if (ecdh_curve_privkey_size(pk_alg_to_curve_id(pk_alg)) != ecdh_key_encoded.size() || - kyber_privkey_size(pk_alg_to_kyber_id(pk_alg)) != kyber_key_encoded.size()) { + kyber_privkey_size() != kyber_key_encoded.size()) { RNP_LOG("ecdh or kyber key length mismatch"); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); } @@ -270,9 +270,8 @@ pgp_kyber_ecdh_composite_private_key_t::pgp_kyber_ecdh_composite_private_key_t( size_t pgp_kyber_ecdh_composite_private_key_t::encoded_size(pgp_pubkey_alg_t pk_alg) { - kyber_parameter_e kyber_param = pk_alg_to_kyber_id(pk_alg); - pgp_curve_t curve = pk_alg_to_curve_id(pk_alg); - return ecdh_curve_privkey_size(curve) + kyber_privkey_size(kyber_param); + pgp_curve_t curve = pk_alg_to_curve_id(pk_alg); + return ecdh_curve_privkey_size(curve) + kyber_privkey_size(); } void @@ -336,10 +335,12 @@ hashed_ecc_keyshare(const std::vector &key_share, } // namespace rnp_result_t -pgp_kyber_ecdh_composite_private_key_t::decrypt(rnp::RNG * rng, - uint8_t * out, - size_t * out_len, - const pgp_kyber_ecdh_encrypted_t *enc) const +pgp_kyber_ecdh_composite_private_key_t::decrypt( + rnp::RNG * rng, + uint8_t * out, + size_t * out_len, + const pgp_kyber_ecdh_encrypted_t *enc, + const std::vector & kyber_pub_encoded) const { initialized_or_throw(); rnp_result_t res; @@ -383,8 +384,10 @@ pgp_kyber_ecdh_composite_private_key_t::decrypt(rnp::RNG * auto kmac = rnp::KMAC256::create(); kmac->compute(hashed_ecdh_keyshare, ecdh_encapsulated_keyshare, + ecdh_key_->get_pubkey_encoded(rng), kyber_keyshare, kyber_encapsulated_keyshare, + kyber_pub_encoded, pk_alg(), kek_vec); Botan::SymmetricKey kek(kek_vec); @@ -527,8 +530,10 @@ pgp_kyber_ecdh_composite_public_key_t::encrypt(rnp::RNG * rng, auto kmac = rnp::KMAC256::create(); kmac->compute(ecdh_hashed_symmetric_key, ecdh_ciphertext, + ecdh_key_.get_encoded(), kyber_encap.symmetric_key, kyber_encap.ciphertext, + kyber_key_.get_encoded(), pk_alg(), kek_vec); Botan::SymmetricKey kek(kek_vec); @@ -563,6 +568,14 @@ pgp_kyber_ecdh_composite_public_key_t::get_encoded() const return result; }; +std::vector +pgp_kyber_ecdh_composite_public_key_t::get_kyber_encoded() const +{ + initialized_or_throw(); + + return kyber_key_.get_encoded(); +}; + bool pgp_kyber_ecdh_composite_public_key_t::is_valid(rnp::RNG *rng) const { diff --git a/src/lib/crypto/kyber_ecdh_composite.h b/src/lib/crypto/kyber_ecdh_composite.h index a798dd13a3..7e54d43c5a 100644 --- a/src/lib/crypto/kyber_ecdh_composite.h +++ b/src/lib/crypto/kyber_ecdh_composite.h @@ -99,7 +99,8 @@ class pgp_kyber_ecdh_composite_private_key_t : public pgp_kyber_ecdh_composite_k rnp_result_t decrypt(rnp::RNG * rng, uint8_t * out, size_t * out_len, - const pgp_kyber_ecdh_encrypted_t *enc) const; + const pgp_kyber_ecdh_encrypted_t *enc, + const std::vector & kyber_pub_encoded) const; bool is_valid(rnp::RNG *rng) const; std::vector get_encoded() const; @@ -152,6 +153,7 @@ class pgp_kyber_ecdh_composite_public_key_t : public pgp_kyber_ecdh_composite_ke bool is_valid(rnp::RNG *rng) const; std::vector get_encoded() const; + std::vector get_kyber_encoded() const; pgp_pubkey_alg_t pk_alg() const diff --git a/src/lib/key_material.cpp b/src/lib/key_material.cpp index fa248ca2b4..3241591f1b 100644 --- a/src/lib/key_material.cpp +++ b/src/lib/key_material.cpp @@ -1751,7 +1751,8 @@ MlkemEcdhKeyMaterial::decrypt(rnp::SecurityContext & ctx, size_t & out_len, const pgp_encrypted_material_t &in) const { - return key_.priv.decrypt(&ctx.rng, out, &out_len, &in.kyber_ecdh); + return key_.priv.decrypt( + &ctx.rng, out, &out_len, &in.kyber_ecdh, pub().get_kyber_encoded()); } size_t diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp index 6b4e07134f..199d29067b 100644 --- a/src/tests/ffi-enc.cpp +++ b/src/tests/ffi-enc.cpp @@ -913,16 +913,8 @@ TEST_F(rnp_tests, test_ffi_decrypt_v6_pkesk_test_vector) } #if defined(ENABLE_PQC) -// NOTE: this tests ML-KEM-ipd test vectors -// The final implementation of the PQC draft implementation will use the final NIST standard. TEST_F(rnp_tests, test_ffi_decrypt_pqc_pkesk_test_vector) { - bool expect_success = true; -#if !(defined(BOTAN_HAS_ML_KEM_INITIAL_PUBLIC_DRAFT) && defined(ENABLE_PQC_MLKEM_IPD)) - // we can only verify the test vectors with ML-KEM-ipd - expect_success = false; -#endif - rnp_ffi_t ffi = NULL; rnp_input_t input = NULL; rnp_output_t output = NULL; @@ -935,12 +927,8 @@ TEST_F(rnp_tests, test_ffi_decrypt_pqc_pkesk_test_vector) assert_rnp_success( rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-seipdv2.asc")); assert_non_null(input); - if (expect_success) { - assert_rnp_success(rnp_decrypt(ffi, input, output)); - assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); - } else { - assert_rnp_failure(rnp_decrypt(ffi, input, output)); - } + assert_rnp_success(rnp_decrypt(ffi, input, output)); + assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); assert_int_equal(unlink("decrypted"), 0); rnp_input_destroy(input); rnp_output_destroy(output); @@ -949,12 +937,8 @@ TEST_F(rnp_tests, test_ffi_decrypt_pqc_pkesk_test_vector) assert_rnp_success( rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v4-seipdv1.asc")); assert_non_null(input); - if (expect_success) { - assert_rnp_success(rnp_decrypt(ffi, input, output)); - assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); - } else { - assert_rnp_failure(rnp_decrypt(ffi, input, output)); - } + assert_rnp_success(rnp_decrypt(ffi, input, output)); + assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); assert_int_equal(unlink("decrypted"), 0); rnp_input_destroy(input); rnp_output_destroy(output); @@ -963,12 +947,9 @@ TEST_F(rnp_tests, test_ffi_decrypt_pqc_pkesk_test_vector) assert_rnp_success( rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v4-seipdv1.asc")); assert_non_null(input); - if (expect_success) { - assert_rnp_success(rnp_decrypt(ffi, input, output)); - assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); - } else { - assert_rnp_failure(rnp_decrypt(ffi, input, output)); - } + assert_rnp_success(rnp_decrypt(ffi, input, output)); + assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); + assert_int_equal(unlink("decrypted"), 0); rnp_input_destroy(input); rnp_output_destroy(output); diff --git a/src/tests/ffi-key.cpp b/src/tests/ffi-key.cpp index 9a71cb0bb9..5525efe588 100644 --- a/src/tests/ffi-key.cpp +++ b/src/tests/ffi-key.cpp @@ -3250,8 +3250,6 @@ TEST_F(rnp_tests, test_ffi_v6_cert_import) } #if defined(ENABLE_PQC) -// NOTE: this tests ML-KEM-ipd test vectors -// The final implementation of the PQC draft implementation will use the final NIST standard. TEST_F(rnp_tests, test_ffi_pqc_certs) { rnp_ffi_t ffi = NULL; @@ -3288,7 +3286,6 @@ TEST_F(rnp_tests, test_ffi_pqc_certs) assert_int_equal(keycount, 2); rnp_ffi_destroy(ffi); } - #endif TEST_F(rnp_tests, test_ffi_v6_seckey_import) From cb08bbcf82354ea8e0d63111d21f43dc0b5252b7 Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Wed, 2 Oct 2024 13:59:57 +0200 Subject: [PATCH 05/15] update C-R code to botan 3.6.0 --- src/lib/CMakeLists.txt | 3 +++ src/lib/crypto/ec.cpp | 14 ++++--------- src/lib/crypto/exdsa_ecdhkem.cpp | 34 +++++++++++++------------------- src/lib/crypto/exdsa_ecdhkem.h | 10 +++++----- src/lib/crypto/x25519.cpp | 8 ++++---- 5 files changed, 30 insertions(+), 39 deletions(-) diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index b6c7496ae2..d4d4066b78 100755 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -34,6 +34,9 @@ find_package(JSON-C 0.11 REQUIRED) if(ENABLE_PQC) find_package(Botan 3.6.0 REQUIRED) set(CRYPTO_BACKEND_BOTAN3 1) +elseif(ENABLE_CRYPTO_REFRESH) + find_package(Botan 3.6.0 REQUIRED) + set(CRYPTO_BACKEND_BOTAN3 1) else() if (CRYPTO_BACKEND_BOTAN3) find_package(Botan 3.0.0 REQUIRED) diff --git a/src/lib/crypto/ec.cpp b/src/lib/crypto/ec.cpp index 459a0693e5..9b703802b8 100644 --- a/src/lib/crypto/ec.cpp +++ b/src/lib/crypto/ec.cpp @@ -232,21 +232,15 @@ ec_generate_generic_native(rnp::RNG * rng, const ec_curve_desc_t *ec_desc = get_curve_desc(curve); const size_t curve_order = BITS_TO_BYTES(ec_desc->bitlen); - Botan::ECDH_PrivateKey privkey_botan(*(rng->obj()), Botan::EC_Group(ec_desc->botan_name)); - Botan::BigInt pub_x = privkey_botan.public_point().get_affine_x(); - Botan::BigInt pub_y = privkey_botan.public_point().get_affine_y(); - Botan::BigInt x = privkey_botan.private_value(); + Botan::ECDH_PrivateKey privkey_botan(*(rng->obj()), + Botan::EC_Group::from_name(ec_desc->botan_name)); // pubkey: 0x04 || X || Y - pubkey = Botan::unlock(Botan::BigInt::encode_fixed_length_int_pair( - pub_x, pub_y, curve_order)); // zero-pads to the given size + pubkey = Botan::unlock(privkey_botan.public_point().xy_bytes()); pubkey.insert(pubkey.begin(), 0x04); privkey = std::vector(curve_order); - x.binary_encode(privkey.data(), privkey.size()); // zero-pads to the given size - - assert(pubkey.size() == 2 * curve_order + 1); - assert(privkey.size() == curve_order); + privkey_botan.private_value().serialize_to(privkey); // zero-pads to the given size return RNP_SUCCESS; } diff --git a/src/lib/crypto/exdsa_ecdhkem.cpp b/src/lib/crypto/exdsa_ecdhkem.cpp index 4d8d47287c..46abc47219 100644 --- a/src/lib/crypto/exdsa_ecdhkem.cpp +++ b/src/lib/crypto/exdsa_ecdhkem.cpp @@ -71,7 +71,7 @@ ecdh_kem_private_key_t::botan_key_ecdh(rnp::RNG *rng) const assert(curve_ >= PGP_CURVE_NIST_P_256 && curve_ <= PGP_CURVE_P256K1); const ec_curve_desc_t *ec_desc = get_curve_desc(curve_); return Botan::ECDH_PrivateKey( - *(rng->obj()), Botan::EC_Group(ec_desc->botan_name), Botan::BigInt(key_)); + *(rng->obj()), Botan::EC_Group::from_name(ec_desc->botan_name), Botan::BigInt(key_)); } Botan::ECDH_PublicKey @@ -80,25 +80,22 @@ ecdh_kem_public_key_t::botan_key_ecdh(rnp::RNG *rng) const assert(curve_ >= PGP_CURVE_NIST_P_256 && curve_ <= PGP_CURVE_P256K1); const ec_curve_desc_t *ec_desc = get_curve_desc(curve_); - Botan::EC_Group group(ec_desc->botan_name); - const size_t curve_order = BITS_TO_BYTES(ec_desc->bitlen); - Botan::BigInt x(key_.data() + 1, curve_order); - Botan::BigInt y(key_.data() + 1 + curve_order, curve_order); - return Botan::ECDH_PublicKey(group, group.point(x, y)); + Botan::EC_Group group = Botan::EC_Group::from_name(ec_desc->botan_name); + return Botan::ECDH_PublicKey(group, Botan::EC_AffinePoint(group, key_).to_legacy_point()); } -Botan::Curve25519_PrivateKey +Botan::X25519_PrivateKey ecdh_kem_private_key_t::botan_key_x25519() const { assert(curve_ == PGP_CURVE_25519); - return Botan::Curve25519_PrivateKey(key_); + return Botan::X25519_PrivateKey(key_); } -Botan::Curve25519_PublicKey +Botan::X25519_PublicKey ecdh_kem_public_key_t::botan_key_x25519() const { assert(curve_ == PGP_CURVE_25519); - return Botan::Curve25519_PublicKey(key_); + return Botan::X25519_PublicKey(key_); } std::vector @@ -119,7 +116,7 @@ ecdh_kem_public_key_t::encapsulate(rnp::RNG * rng, std::vector &symmetric_key) const { if (curve_ == PGP_CURVE_25519) { - Botan::Curve25519_PrivateKey eph_prv_key(*(rng->obj())); + Botan::X25519_PrivateKey eph_prv_key(*(rng->obj())); ciphertext = eph_prv_key.public_value(); Botan::PK_Key_Agreement key_agreement(eph_prv_key, *(rng->obj()), "Raw"); symmetric_key = Botan::unlock(key_agreement.derive_key(0, key_).bits_of()); @@ -130,7 +127,7 @@ ecdh_kem_public_key_t::encapsulate(rnp::RNG * rng, return RNP_ERROR_NOT_SUPPORTED; } - Botan::EC_Group domain(curve_desc->botan_name); + Botan::EC_Group domain = Botan::EC_Group::from_name(curve_desc->botan_name); Botan::ECDH_PrivateKey eph_prv_key(*(rng->obj()), domain); Botan::PK_Key_Agreement key_agreement(eph_prv_key, *(rng->obj()), "Raw"); ciphertext = eph_prv_key.public_value(); @@ -145,8 +142,8 @@ ecdh_kem_private_key_t::decapsulate(rnp::RNG * rng, std::vector & plaintext) { if (curve_ == PGP_CURVE_25519) { - Botan::Curve25519_PrivateKey priv_key = botan_key_x25519(); - Botan::PK_Key_Agreement key_agreement(priv_key, *(rng->obj()), "Raw"); + Botan::X25519_PrivateKey priv_key = botan_key_x25519(); + Botan::PK_Key_Agreement key_agreement(priv_key, *(rng->obj()), "Raw"); plaintext = Botan::unlock(key_agreement.derive_key(0, ciphertext).bits_of()); } else { Botan::ECDH_PrivateKey priv_key = botan_key_ecdh(rng); @@ -213,7 +210,7 @@ exdsa_private_key_t::botan_key(rnp::RNG *rng) const { const ec_curve_desc_t * ec_desc = get_curve_desc(curve_); Botan::ECDSA_PrivateKey priv_key( - *(rng->obj()), Botan::EC_Group(ec_desc->botan_name), Botan::BigInt(key_)); + *(rng->obj()), Botan::EC_Group::from_name(ec_desc->botan_name), Botan::BigInt(key_)); return priv_key; } @@ -222,11 +219,8 @@ exdsa_public_key_t::botan_key() const { // format: 04 | X | Y const ec_curve_desc_t *ec_desc = get_curve_desc(curve_); - Botan::EC_Group group(ec_desc->botan_name); - const size_t curve_order = BITS_TO_BYTES(ec_desc->bitlen); - Botan::BigInt x(key_.data() + 1, curve_order); - Botan::BigInt y(key_.data() + 1 + curve_order, curve_order); - return Botan::ECDSA_PublicKey(group, group.point(x, y)); + Botan::EC_Group group = Botan::EC_Group::from_name(ec_desc->botan_name); + return Botan::ECDSA_PublicKey(group, Botan::EC_AffinePoint(group, key_).to_legacy_point()); } /* NOTE hash_alg unused for ed25519/x25519 curves */ diff --git a/src/lib/crypto/exdsa_ecdhkem.h b/src/lib/crypto/exdsa_ecdhkem.h index 6fe2ab0283..815a2690e0 100644 --- a/src/lib/crypto/exdsa_ecdhkem.h +++ b/src/lib/crypto/exdsa_ecdhkem.h @@ -38,7 +38,7 @@ #include #include #include -#include +#include struct ecdh_kem_key_t; /* forward declaration */ struct exdsa_key_t; /* forward declaration */ @@ -91,8 +91,8 @@ class ecdh_kem_public_key_t : public ec_key_t { std::vector &symmetric_key) const; private: - Botan::ECDH_PublicKey botan_key_ecdh(rnp::RNG *rng) const; - Botan::Curve25519_PublicKey botan_key_x25519() const; + Botan::ECDH_PublicKey botan_key_ecdh(rnp::RNG *rng) const; + Botan::X25519_PublicKey botan_key_x25519() const; std::vector key_; }; @@ -118,8 +118,8 @@ class ecdh_kem_private_key_t : public ec_key_t { std::vector & plaintext); private: - Botan::ECDH_PrivateKey botan_key_ecdh(rnp::RNG *rng) const; - Botan::Curve25519_PrivateKey botan_key_x25519() const; + Botan::ECDH_PrivateKey botan_key_ecdh(rnp::RNG *rng) const; + Botan::X25519_PrivateKey botan_key_x25519() const; Botan::secure_vector key_; }; diff --git a/src/lib/crypto/x25519.cpp b/src/lib/crypto/x25519.cpp index 1441260846..9fb2c340f8 100644 --- a/src/lib/crypto/x25519.cpp +++ b/src/lib/crypto/x25519.cpp @@ -25,7 +25,7 @@ */ #include "x25519.h" -#include +#include #if defined(ENABLE_CRYPTO_REFRESH) #include "exdsa_ecdhkem.h" #include "hkdf.hpp" @@ -155,11 +155,11 @@ x25519_validate_key_native(rnp::RNG *rng, const pgp_x25519_key_t *key, bool secr bool valid_pub; bool valid_priv; - Botan::Curve25519_PublicKey pub_key(key->priv); + Botan::X25519_PublicKey pub_key(key->priv); valid_pub = pub_key.check_key(*(rng->obj()), false); if (secret) { - Botan::Curve25519_PrivateKey priv_key( + Botan::X25519_PrivateKey priv_key( Botan::secure_vector(key->priv.begin(), key->priv.end())); valid_priv = priv_key.check_key(*(rng->obj()), false); } else { @@ -177,7 +177,7 @@ generate_x25519_native(rnp::RNG * rng, std::vector &privkey, std::vector &pubkey) { - Botan::Curve25519_PrivateKey priv_key(*(rng->obj())); + Botan::X25519_PrivateKey priv_key(*(rng->obj())); pubkey = priv_key.public_value(); privkey = Botan::unlock(priv_key.raw_private_key_bits()); From aec0f300517598100a965d6e9a05676f1e61fa3c Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Thu, 17 Oct 2024 14:09:47 +0200 Subject: [PATCH 06/15] add X448/Ed448 (PQC and standalone) --- include/repgp/repgp_def.h | 17 +- include/rnp/rnp.h | 4 +- src/lib/CMakeLists.txt | 2 +- src/lib/crypto/dilithium_exdsa_composite.cpp | 23 +- src/lib/crypto/ec.cpp | 27 +- src/lib/crypto/ec.h | 11 +- src/lib/crypto/ec_curves.cpp | 31 +++ src/lib/crypto/ecdh.cpp | 45 ---- src/lib/crypto/ecdh.h | 37 --- src/lib/crypto/ed25519.cpp | 61 +++++ src/lib/crypto/ed25519.h | 15 +- src/lib/crypto/exdsa_ecdhkem.cpp | 128 +++++++-- src/lib/crypto/exdsa_ecdhkem.h | 4 + src/lib/crypto/kyber_ecdh_composite.cpp | 28 +- src/lib/crypto/x25519.cpp | 147 +++++++++-- src/lib/crypto/x25519.h | 19 +- src/lib/generate-key.cpp | 21 +- src/lib/key_material.cpp | 260 ++++++++++++++++++- src/lib/key_material.hpp | 64 +++++ src/lib/pgp-key.cpp | 8 +- src/lib/rnp.cpp | 41 ++- src/lib/types.h | 2 + src/librepgp/stream-common.cpp | 3 +- src/librepgp/stream-dump.cpp | 48 +++- src/librepgp/stream-packet.cpp | 46 +++- src/librepgp/stream-sig.cpp | 20 +- src/rnpkeys/tui.cpp | 7 + src/tests/cipher.cpp | 5 +- src/tests/cli_tests.py | 7 +- src/tests/ffi-enc.cpp | 1 + src/tests/ffi-key.cpp | 2 + src/tests/ffi.cpp | 11 +- src/tests/pqc.cpp | 3 +- 33 files changed, 925 insertions(+), 223 deletions(-) diff --git a/include/repgp/repgp_def.h b/include/repgp/repgp_def.h index 851831a6e8..91414872f7 100644 --- a/include/repgp/repgp_def.h +++ b/include/repgp/repgp_def.h @@ -227,8 +227,10 @@ typedef enum : uint8_t { PGP_PKA_EDDSA = 22, /* EdDSA from draft-ietf-openpgp-rfc4880bis */ #if defined(ENABLE_CRYPTO_REFRESH) - PGP_PKA_X25519 = 25, /* v6 / Crypto Refresh */ - PGP_PKA_ED25519 = 27, /* v6 / Crypto Refresh */ + PGP_PKA_X25519 = 25, + PGP_PKA_X448 = 26, + PGP_PKA_ED25519 = 27, + PGP_PKA_ED448 = 28, #endif PGP_PKA_SM2 = 99, /* SM2 encryption/signature schemes */ @@ -236,14 +238,14 @@ typedef enum : uint8_t { #if defined(ENABLE_PQC) /* PQC-ECC composite */ PGP_PKA_KYBER768_X25519 = 105, - // PGP_PKA_KYBER1024_X448 = 106, + PGP_PKA_KYBER1024_X448 = 106, PGP_PKA_KYBER768_P256 = 111, PGP_PKA_KYBER1024_P384 = 112, PGP_PKA_KYBER768_BP256 = 113, PGP_PKA_KYBER1024_BP384 = 114, PGP_PKA_DILITHIUM3_ED25519 = 107, - // PGP_PKA_DILITHIUM5_ED448 = 108, + PGP_PKA_DILITHIUM5_ED448 = 108, PGP_PKA_DILITHIUM3_P256 = 115, PGP_PKA_DILITHIUM5_P384 = 116, PGP_PKA_DILITHIUM3_BP256 = 117, @@ -258,8 +260,6 @@ typedef enum : uint8_t { PGP_PKA_PRIVATE02 = 102, /* Private/Experimental Algorithm */ PGP_PKA_PRIVATE03 = 103, /* Private/Experimental Algorithm */ PGP_PKA_PRIVATE04 = 104, /* Private/Experimental Algorithm */ - PGP_PKA_PRIVATE06 = 106, /* Private/Experimental Algorithm */ - PGP_PKA_PRIVATE08 = 108, /* Private/Experimental Algorithm */ PGP_PKA_PRIVATE10 = 110 /* Private/Experimental Algorithm */ #else PGP_PKA_PRIVATE00 = 100, /* Private/Experimental Algorithm */ @@ -296,7 +296,10 @@ typedef enum { PGP_CURVE_P256K1, PGP_CURVE_SM2_P_256, - +#if defined(ENABLE_CRYPTO_REFRESH) + PGP_CURVE_ED448, + PGP_CURVE_448, +#endif // Keep always last one PGP_CURVE_MAX } pgp_curve_t; diff --git a/include/rnp/rnp.h b/include/rnp/rnp.h index c305805ae8..30123b4c29 100644 --- a/include/rnp/rnp.h +++ b/include/rnp/rnp.h @@ -4010,9 +4010,11 @@ RNP_API const char *rnp_backend_version(); #define RNP_ALGNAME_ECDH "ECDH" #define RNP_ALGNAME_ECDSA "ECDSA" #define RNP_ALGNAME_EDDSA "EDDSA" -#if defined(RNP_EXPERIMENTAL_CRYPTO_REFRESH) || defined(RNP_EXPERIMENTAL_PQC) +#if defined(RNP_EXPERIMENTAL_CRYPTO_REFRESH) #define RNP_ALGNAME_ED25519 "ED25519" #define RNP_ALGNAME_X25519 "X25519" +#define RNP_ALGNAME_ED448 "ED448" +#define RNP_ALGNAME_X448 "X448" #endif #if defined(RNP_EXPERIMENTAL_PQC) #define RNP_ALGNAME_KYBER768_X25519 "ML-KEM-768+X25519" diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index d4d4066b78..5b70f57d71 100755 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -191,7 +191,7 @@ if(CRYPTO_BACKEND_BOTAN) resolve_feature_state(ENABLE_AEAD "AEAD_EAX;AEAD_OCB") resolve_feature_state(ENABLE_TWOFISH "TWOFISH") resolve_feature_state(ENABLE_IDEA "IDEA") - resolve_feature_state(ENABLE_CRYPTO_REFRESH "HKDF") + resolve_feature_state(ENABLE_CRYPTO_REFRESH "HKDF;ED448;X448") resolve_feature_state(ENABLE_PQC "KMAC;DILITHIUM;KYBER;SPHINCS_PLUS_WITH_SHA2;SPHINCS_PLUS_WITH_SHAKE") resolve_feature_state(ENABLE_BLOWFISH "BLOWFISH") resolve_feature_state(ENABLE_CAST5 "CAST_128") diff --git a/src/lib/crypto/dilithium_exdsa_composite.cpp b/src/lib/crypto/dilithium_exdsa_composite.cpp index f097950e74..47e646b542 100644 --- a/src/lib/crypto/dilithium_exdsa_composite.cpp +++ b/src/lib/crypto/dilithium_exdsa_composite.cpp @@ -74,9 +74,8 @@ pgp_dilithium_exdsa_composite_key_t::exdsa_curve_privkey_size(pgp_curve_t curve) switch (curve) { case PGP_CURVE_ED25519: return 32; - /* TODO */ - // case PGP_CURVE_ED448: - // return 56; + case PGP_CURVE_ED448: + return 57; case PGP_CURVE_NIST_P_256: return 32; case PGP_CURVE_NIST_P_384: @@ -97,9 +96,8 @@ pgp_dilithium_exdsa_composite_key_t::exdsa_curve_pubkey_size(pgp_curve_t curve) switch (curve) { case PGP_CURVE_ED25519: return 32; - /* TODO */ - // case PGP_CURVE_ED448: - // return 56; + case PGP_CURVE_ED448: + return 57; case PGP_CURVE_NIST_P_256: return 65; case PGP_CURVE_NIST_P_384: @@ -120,9 +118,8 @@ pgp_dilithium_exdsa_composite_key_t::exdsa_curve_signature_size(pgp_curve_t curv switch (curve) { case PGP_CURVE_ED25519: return 64; - /* TODO */ - // case PGP_CURVE_ED448: - // return 114; + case PGP_CURVE_ED448: + return 114; case PGP_CURVE_NIST_P_256: return 64; case PGP_CURVE_NIST_P_384: @@ -150,6 +147,8 @@ pgp_dilithium_exdsa_composite_key_t::pk_alg_to_dilithium_id(pgp_pubkey_alg_t pk_ case PGP_PKA_DILITHIUM5_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: return dilithium_L5; default: RNP_LOG("invalid PK alg given"); @@ -171,8 +170,8 @@ pgp_dilithium_exdsa_composite_key_t::pk_alg_to_curve_id(pgp_pubkey_alg_t pk_alg) return PGP_CURVE_BP384; case PGP_PKA_DILITHIUM5_P384: return PGP_CURVE_NIST_P_384; - /*case PGP_PKA_DILITHIUM5_ED448: - throw rnp::rnp_exception(RNP_ERROR_NOT_IMPLEMENTED);*/ + case PGP_PKA_DILITHIUM5_ED448: + return PGP_CURVE_ED448; default: RNP_LOG("invalid PK alg given"); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); @@ -272,7 +271,7 @@ pgp_dilithium_exdsa_composite_private_key_t::pgp_dilithium_exdsa_composite_priva size_t pgp_dilithium_exdsa_composite_private_key_t::encoded_size(pgp_pubkey_alg_t pk_alg) { - pgp_curve_t curve = pk_alg_to_curve_id(pk_alg); + pgp_curve_t curve = pk_alg_to_curve_id(pk_alg); return exdsa_curve_privkey_size(curve) + dilithium_privkey_size(); } diff --git a/src/lib/crypto/ec.cpp b/src/lib/crypto/ec.cpp index 9b703802b8..9f9723cbc2 100644 --- a/src/lib/crypto/ec.cpp +++ b/src/lib/crypto/ec.cpp @@ -221,8 +221,7 @@ static rnp_result_t ec_generate_generic_native(rnp::RNG * rng, std::vector &privkey, std::vector &pubkey, - pgp_curve_t curve, - pgp_pubkey_alg_t alg) + pgp_curve_t curve) { if (!is_generic_prime_curve(curve)) { RNP_LOG("expected generic prime curve"); @@ -249,22 +248,20 @@ rnp_result_t ec_generate_native(rnp::RNG * rng, std::vector &privkey, std::vector &pubkey, - pgp_curve_t curve, - pgp_pubkey_alg_t alg) + pgp_curve_t curve) { - if (curve == PGP_CURVE_25519) { + switch (curve) { + case PGP_CURVE_25519: return generate_x25519_native(rng, privkey, pubkey); - } else if (curve == PGP_CURVE_ED25519) { + case PGP_CURVE_ED25519: return generate_ed25519_native(rng, privkey, pubkey); - } else if (is_generic_prime_curve(curve)) { - if (alg != PGP_PKA_ECDH && alg != PGP_PKA_ECDSA) { - RNP_LOG("alg and curve mismatch"); - return RNP_ERROR_BAD_PARAMETERS; - } - return ec_generate_generic_native(rng, privkey, pubkey, curve, alg); - } else { - RNP_LOG("invalid curve"); - return RNP_ERROR_BAD_PARAMETERS; + case PGP_CURVE_448: + return generate_x448_native(rng, privkey, pubkey); + case PGP_CURVE_ED448: + return generate_ed448_native(rng, privkey, pubkey); + default: + break; } + return ec_generate_generic_native(rng, privkey, pubkey, curve); } #endif diff --git a/src/lib/crypto/ec.h b/src/lib/crypto/ec.h index d96508dc87..d9aa7d8115 100644 --- a/src/lib/crypto/ec.h +++ b/src/lib/crypto/ec.h @@ -124,6 +124,9 @@ typedef struct pgp_ed25519_signature_t { std::vector sig; // native encoding } pgp_ed25519_signature_t; +typedef pgp_ed25519_key_t pgp_ed448_key_t; +typedef pgp_ed25519_signature_t pgp_ed448_signature_t; + typedef struct pgp_x25519_key_t { std::vector pub; // \ native encoding std::vector priv; // / @@ -141,10 +144,14 @@ typedef struct pgp_x25519_key_t { } } pgp_x25519_key_t; +typedef pgp_x25519_key_t pgp_x448_key_t; + typedef struct pgp_x25519_encrypted_t { std::vector eph_key; std::vector enc_sess_key; } pgp_x25519_encrypted_t; + +typedef pgp_x25519_encrypted_t pgp_x448_encrypted_t; #endif /* @@ -235,13 +242,11 @@ bool x25519_bits_tweaked(const pgp_ec_key_t &key); * @param privkey private key to be generated * @param pubkey public key to be generated * @param curve chosen curve - * @param alg algorithm id * * @returns RNP_ERROR_BAD_PARAMETERS if the curve or alg parameter is invalid. */ rnp_result_t ec_generate_native(rnp::RNG * rng, std::vector &privkey, std::vector &pubkey, - pgp_curve_t curve, - pgp_pubkey_alg_t alg); + pgp_curve_t curve); #endif diff --git a/src/lib/crypto/ec_curves.cpp b/src/lib/crypto/ec_curves.cpp index db5cf090fe..e9e00b97fa 100644 --- a/src/lib/crypto/ec_curves.cpp +++ b/src/lib/crypto/ec_curves.cpp @@ -263,6 +263,37 @@ static const ec_curve_desc_t ec_curves[] = { "0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", "0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", }, +/* TODO: correct values */ +#if defined(ENABLE_CRYPTO_REFRESH) + {PGP_CURVE_ED448, + 0, + {}, + 0, + "Ed25519", + "Ed25519", + true, + "0x00", + "0x00", + "0x00", + "0x00", + "0x00", + "0x00", + "0x00"}, + {PGP_CURVE_448, + 0, + {}, + 0x00, + "curve25519", + "Curve25519", + true, + "0x00", + "0x00", + "0x00", + "0x00", + "0x00", + "0x00", + "0x00"}, +#endif }; pgp_curve_t diff --git a/src/lib/crypto/ecdh.cpp b/src/lib/crypto/ecdh.cpp index 206e352a97..f2b4ca0492 100644 --- a/src/lib/crypto/ecdh.cpp +++ b/src/lib/crypto/ecdh.cpp @@ -389,48 +389,3 @@ ecdh_decrypt_pkcs5(uint8_t * out, botan_privkey_destroy(prv_key); return ret; } - -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) -rnp_result_t -ecdh_kem_gen_keypair_native(rnp::RNG * rng, - std::vector &privkey, - std::vector &pubkey, - pgp_curve_t curve) -{ - return ec_generate_native(rng, privkey, pubkey, curve, PGP_PKA_ECDH); -} - -rnp_result_t -exdsa_gen_keypair_native(rnp::RNG * rng, - std::vector &privkey, - std::vector &pubkey, - pgp_curve_t curve) -{ - pgp_pubkey_alg_t alg; - switch (curve) { - case PGP_CURVE_ED25519: - alg = PGP_PKA_EDDSA; - break; - case PGP_CURVE_NIST_P_256: - FALLTHROUGH_STATEMENT; - case PGP_CURVE_NIST_P_384: - FALLTHROUGH_STATEMENT; - case PGP_CURVE_NIST_P_521: - FALLTHROUGH_STATEMENT; - case PGP_CURVE_BP256: - FALLTHROUGH_STATEMENT; - case PGP_CURVE_BP384: - FALLTHROUGH_STATEMENT; - case PGP_CURVE_BP512: - FALLTHROUGH_STATEMENT; - case PGP_CURVE_P256K1: - alg = PGP_PKA_ECDSA; - break; - default: - RNP_LOG("invalid curve for ECDSA/EDDSA"); - return RNP_ERROR_BAD_PARAMETERS; - } - return ec_generate_native(rng, privkey, pubkey, curve, alg); -} - -#endif diff --git a/src/lib/crypto/ecdh.h b/src/lib/crypto/ecdh.h index ef9a00d7f4..9bd9356dc9 100644 --- a/src/lib/crypto/ecdh.h +++ b/src/lib/crypto/ecdh.h @@ -116,41 +116,4 @@ rnp_result_t ecdh_decrypt_pkcs5(uint8_t * out, const pgp_ec_key_t * key, const pgp_fingerprint_t & fingerprint); -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) -/* Generate an ECDH key pair in "native" format, i.e., - * no changes to the format specified in the respective standard - * are applied (uncompressed SEC1 and RFC 7748). - * - * @param rng initialized rnp::RNG object - * @param privkey [out] the generated private key - * @param pubkey [out] the generated public key - * @param curve the curve for which a key pair is generated - * - * @return RNP_SUCCESS on success and output parameters are populated - * @return RNP_ERROR_BAD_PARAMETERS unexpected input provided - */ -rnp_result_t ecdh_kem_gen_keypair_native(rnp::RNG * rng, - std::vector &privkey, - std::vector &pubkey, - pgp_curve_t curve); - -/* Generate an ECDSA or EdDSA key pair in "native" format, i.e., - * no changes to the format specified in the respective standard - * are applied (uncompressed SEC1 and RFC 7748). - * - * @param rng initialized rnp::RNG object - * @param privkey [out] the generated private key - * @param pubkey [out] the generated public key - * @param curve the curve for which a key pair is generated - * - * @return RNP_SUCCESS on success and output parameters are populated - * @return RNP_ERROR_BAD_PARAMETERS unexpected input provided - */ -rnp_result_t exdsa_gen_keypair_native(rnp::RNG * rng, - std::vector &privkey, - std::vector &pubkey, - pgp_curve_t curve); - -#endif - #endif // ECDH_H_ diff --git a/src/lib/crypto/ed25519.cpp b/src/lib/crypto/ed25519.cpp index 240d84819c..6ebf82415f 100644 --- a/src/lib/crypto/ed25519.cpp +++ b/src/lib/crypto/ed25519.cpp @@ -30,6 +30,7 @@ #include #include +#include #include rnp_result_t @@ -93,3 +94,63 @@ ed25519_validate_key_native(rnp::RNG *rng, const pgp_ed25519_key_t *key, bool se return RNP_SUCCESS; } + +rnp_result_t +generate_ed448_native(rnp::RNG * rng, + std::vector &privkey, + std::vector &pubkey) +{ + Botan::Ed448_PrivateKey private_key(*(rng->obj())); + const size_t key_len = 57; + auto priv = Botan::unlock(private_key.raw_private_key_bits()); + assert(priv.size() == key_len); + auto pub = private_key.public_key_bits(); + assert(pub.size() == key_len); + privkey = std::vector(priv.begin(), priv.begin() + key_len); + pubkey = std::vector(pub.begin(), pub.begin() + key_len); + return RNP_SUCCESS; +} + +rnp_result_t +ed448_sign_native(rnp::RNG * rng, + std::vector & sig_out, + const std::vector &key, + const uint8_t * hash, + size_t hash_len) +{ + Botan::Ed448_PrivateKey priv_key(Botan::secure_vector(key.begin(), key.end())); + auto signer = Botan::PK_Signer(priv_key, *(rng->obj()), "Pure"); + sig_out = signer.sign_message(hash, hash_len, *(rng->obj())); + return RNP_SUCCESS; +} + +rnp_result_t +ed448_verify_native(const std::vector &sig, + const std::vector &key, + const uint8_t * hash, + size_t hash_len) +{ + Botan::Ed448_PublicKey pub_key(key); + auto verifier = Botan::PK_Verifier(pub_key, "Pure"); + if (verifier.verify_message(hash, hash_len, sig.data(), sig.size())) { + return RNP_SUCCESS; + } + return RNP_ERROR_VERIFICATION_FAILED; +} + +rnp_result_t +ed448_validate_key_native(rnp::RNG *rng, const pgp_ed448_key_t *key, bool secret) +{ + Botan::Ed448_PublicKey pub_key(key->pub); + if (!pub_key.check_key(*(rng->obj()), false)) { + return RNP_ERROR_BAD_PARAMETERS; + } + if (secret) { + Botan::Ed448_PrivateKey priv_key( + Botan::secure_vector(key->priv.begin(), key->priv.end())); + if (!priv_key.check_key(*(rng->obj()), false)) { + return RNP_ERROR_SIGNING_FAILED; + } + } + return RNP_SUCCESS; +} \ No newline at end of file diff --git a/src/lib/crypto/ed25519.h b/src/lib/crypto/ed25519.h index 5ab109d07a..ada0de2c9d 100644 --- a/src/lib/crypto/ed25519.h +++ b/src/lib/crypto/ed25519.h @@ -34,7 +34,7 @@ #include "crypto/rng.h" #include "crypto/ec.h" -/* implements ED25519 with native format (V6 and PQC) */ +/* implements Ed25519 and Ed448 with native format (V6 and PQC) */ rnp_result_t generate_ed25519_native(rnp::RNG * rng, std::vector &privkey, @@ -55,4 +55,17 @@ rnp_result_t ed25519_validate_key_native(rnp::RNG * rng, const pgp_ed25519_key_t *key, bool secret); +rnp_result_t generate_ed448_native(rnp::RNG * rng, + std::vector &privkey, + std::vector &pubkey); +rnp_result_t ed448_sign_native(rnp::RNG * rng, + std::vector & sig_out, + const std::vector &key, + const uint8_t * hash, + size_t hash_len); +rnp_result_t ed448_verify_native(const std::vector &sig, + const std::vector &key, + const uint8_t * hash, + size_t hash_len); +rnp_result_t ed448_validate_key_native(rnp::RNG *rng, const pgp_ed448_key_t *key, bool secret); #endif diff --git a/src/lib/crypto/exdsa_ecdhkem.cpp b/src/lib/crypto/exdsa_ecdhkem.cpp index 46abc47219..73b364f593 100644 --- a/src/lib/crypto/exdsa_ecdhkem.cpp +++ b/src/lib/crypto/exdsa_ecdhkem.cpp @@ -101,13 +101,34 @@ ecdh_kem_public_key_t::botan_key_x25519() const std::vector ecdh_kem_private_key_t::get_pubkey_encoded(rnp::RNG *rng) const { - if (curve_ == PGP_CURVE_25519) { + switch (curve_) { + case PGP_CURVE_25519: { Botan::X25519_PrivateKey botan_key = botan_key_x25519(); return botan_key.public_value(); - } else { + } + case PGP_CURVE_448: { + Botan::X448_PrivateKey botan_key = botan_key_x448(); + return botan_key.public_value(); + } + default: { Botan::ECDH_PrivateKey botan_key = botan_key_ecdh(rng); return botan_key.public_value(); } + } +} + +Botan::X448_PrivateKey +ecdh_kem_private_key_t::botan_key_x448() const +{ + assert(curve_ == PGP_CURVE_448); + return Botan::X448_PrivateKey(key_); +} + +Botan::X448_PublicKey +ecdh_kem_public_key_t::botan_key_x448() const +{ + assert(curve_ == PGP_CURVE_448); + return Botan::X448_PublicKey(key_); } rnp_result_t @@ -115,12 +136,22 @@ ecdh_kem_public_key_t::encapsulate(rnp::RNG * rng, std::vector &ciphertext, std::vector &symmetric_key) const { - if (curve_ == PGP_CURVE_25519) { + switch (curve_) { + case PGP_CURVE_25519: { Botan::X25519_PrivateKey eph_prv_key(*(rng->obj())); ciphertext = eph_prv_key.public_value(); Botan::PK_Key_Agreement key_agreement(eph_prv_key, *(rng->obj()), "Raw"); symmetric_key = Botan::unlock(key_agreement.derive_key(0, key_).bits_of()); - } else { + break; + } + case PGP_CURVE_448: { + Botan::X448_PrivateKey eph_prv_key(*(rng->obj())); + ciphertext = eph_prv_key.public_value(); + Botan::PK_Key_Agreement key_agreement(eph_prv_key, *(rng->obj()), "Raw"); + symmetric_key = Botan::unlock(key_agreement.derive_key(0, key_).bits_of()); + break; + } + default: { const ec_curve_desc_t *curve_desc = get_curve_desc(curve_); if (!curve_desc) { RNP_LOG("unknown curve"); @@ -132,6 +163,8 @@ ecdh_kem_public_key_t::encapsulate(rnp::RNG * rng, Botan::PK_Key_Agreement key_agreement(eph_prv_key, *(rng->obj()), "Raw"); ciphertext = eph_prv_key.public_value(); symmetric_key = Botan::unlock(key_agreement.derive_key(0, key_).bits_of()); + break; + } } return RNP_SUCCESS; } @@ -141,14 +174,25 @@ ecdh_kem_private_key_t::decapsulate(rnp::RNG * rng, const std::vector &ciphertext, std::vector & plaintext) { - if (curve_ == PGP_CURVE_25519) { + switch (curve_) { + case PGP_CURVE_25519: { Botan::X25519_PrivateKey priv_key = botan_key_x25519(); Botan::PK_Key_Agreement key_agreement(priv_key, *(rng->obj()), "Raw"); plaintext = Botan::unlock(key_agreement.derive_key(0, ciphertext).bits_of()); - } else { + break; + } + case PGP_CURVE_448: { + Botan::X448_PrivateKey priv_key = botan_key_x448(); + Botan::PK_Key_Agreement key_agreement(priv_key, *(rng->obj()), "Raw"); + plaintext = Botan::unlock(key_agreement.derive_key(0, ciphertext).bits_of()); + break; + } + default: { Botan::ECDH_PrivateKey priv_key = botan_key_ecdh(rng); Botan::PK_Key_Agreement key_agreement(priv_key, *(rng->obj()), "Raw"); plaintext = Botan::unlock(key_agreement.derive_key(0, ciphertext).bits_of()); + break; + } } return RNP_SUCCESS; } @@ -157,7 +201,7 @@ rnp_result_t ec_key_t::generate_ecdh_kem_key_pair(rnp::RNG *rng, ecdh_kem_key_t *out, pgp_curve_t curve) { std::vector pub, priv; - rnp_result_t result = ecdh_kem_gen_keypair_native(rng, priv, pub, curve); + rnp_result_t result = ec_generate_native(rng, priv, pub, curve); if (result != RNP_SUCCESS) { RNP_LOG("error when generating EC key pair"); return result; @@ -193,7 +237,7 @@ rnp_result_t ec_key_t::generate_exdsa_key_pair(rnp::RNG *rng, exdsa_key_t *out, pgp_curve_t curve) { std::vector pub, priv; - rnp_result_t result = exdsa_gen_keypair_native(rng, priv, pub, curve); + rnp_result_t result = ec_generate_native(rng, priv, pub, curve); if (result != RNP_SUCCESS) { RNP_LOG("error when generating EC key pair"); return result; @@ -223,7 +267,7 @@ exdsa_public_key_t::botan_key() const return Botan::ECDSA_PublicKey(group, Botan::EC_AffinePoint(group, key_).to_legacy_point()); } -/* NOTE hash_alg unused for ed25519/x25519 curves */ +/* NOTE hash_alg unused for Ed/X curves */ rnp_result_t exdsa_private_key_t::sign(rnp::RNG * rng, std::vector &sig_out, @@ -231,15 +275,21 @@ exdsa_private_key_t::sign(rnp::RNG * rng, size_t hash_len, pgp_hash_alg_t hash_alg) const { - if (curve_ == PGP_CURVE_ED25519) { + switch (curve_) { + case PGP_CURVE_ED25519: { return ed25519_sign_native(rng, sig_out, Botan::unlock(key_), hash, hash_len); - } else { + } + case PGP_CURVE_ED448: { + return ed448_sign_native(rng, sig_out, Botan::unlock(key_), hash, hash_len); + } + default: { Botan::ECDSA_PrivateKey priv_key = botan_key(rng); auto signer = Botan::PK_Signer(priv_key, *(rng->obj()), ecdsa_padding_str_for(hash_alg)); sig_out = signer.sign_message(hash, hash_len, *(rng->obj())); + return RNP_SUCCESS; + } } - return RNP_SUCCESS; } rnp_result_t @@ -248,62 +298,96 @@ exdsa_public_key_t::verify(const std::vector &sig, size_t hash_len, pgp_hash_alg_t hash_alg) const { - if (curve_ == PGP_CURVE_ED25519) { + switch (curve_) { + case PGP_CURVE_ED25519: { return ed25519_verify_native(sig, key_, hash, hash_len); - } else { + } + case PGP_CURVE_ED448: { + return ed448_verify_native(sig, key_, hash, hash_len); + } + default: { Botan::ECDSA_PublicKey pub_key = botan_key(); auto verifier = Botan::PK_Verifier(pub_key, ecdsa_padding_str_for(hash_alg)); if (verifier.verify_message(hash, hash_len, sig.data(), sig.size())) { return RNP_SUCCESS; } } + } return RNP_ERROR_VERIFICATION_FAILED; } bool exdsa_public_key_t::is_valid(rnp::RNG *rng) const { - if (curve_ == PGP_CURVE_ED25519) { + switch (curve_) { + case PGP_CURVE_ED25519: { Botan::Ed25519_PublicKey pub_key(key_); return pub_key.check_key(*(rng->obj()), false); - } else { + } + case PGP_CURVE_ED448: { + Botan::Ed448_PublicKey pub_key(key_); + return pub_key.check_key(*(rng->obj()), false); + } + default: { Botan::ECDSA_PublicKey pub_key = botan_key(); return pub_key.check_key(*(rng->obj()), false); } + } } bool exdsa_private_key_t::is_valid(rnp::RNG *rng) const { - if (curve_ == PGP_CURVE_ED25519) { + switch (curve_) { + case PGP_CURVE_ED25519: { Botan::Ed25519_PrivateKey priv_key(key_); return priv_key.check_key(*(rng->obj()), false); - } else { + } + case PGP_CURVE_ED448: { + Botan::Ed448_PrivateKey priv_key(key_); + return priv_key.check_key(*(rng->obj()), false); + } + default: { auto priv_key = botan_key(rng); return priv_key.check_key(*(rng->obj()), false); } + } } bool ecdh_kem_public_key_t::is_valid(rnp::RNG *rng) const { - if (curve_ == PGP_CURVE_25519) { + switch (curve_) { + case PGP_CURVE_25519: { auto pub_key = botan_key_x25519(); return pub_key.check_key(*(rng->obj()), false); - } else { + } + case PGP_CURVE_448: { + auto pub_key = botan_key_x448(); + return pub_key.check_key(*(rng->obj()), false); + } + default: { auto pub_key = botan_key_ecdh(rng); return pub_key.check_key(*(rng->obj()), false); } + } } bool ecdh_kem_private_key_t::is_valid(rnp::RNG *rng) const { - if (curve_ == PGP_CURVE_25519) { + switch (curve_) { + case PGP_CURVE_25519: { auto priv_key = botan_key_x25519(); return priv_key.check_key(*(rng->obj()), false); - } else { + } + case PGP_CURVE_448: { + auto priv_key = botan_key_x448(); + return priv_key.check_key(*(rng->obj()), false); + } + default: { auto priv_key = botan_key_ecdh(rng); return priv_key.check_key(*(rng->obj()), false); } + } } diff --git a/src/lib/crypto/exdsa_ecdhkem.h b/src/lib/crypto/exdsa_ecdhkem.h index 815a2690e0..e5f3862148 100644 --- a/src/lib/crypto/exdsa_ecdhkem.h +++ b/src/lib/crypto/exdsa_ecdhkem.h @@ -39,6 +39,8 @@ #include #include #include +#include +#include struct ecdh_kem_key_t; /* forward declaration */ struct exdsa_key_t; /* forward declaration */ @@ -93,6 +95,7 @@ class ecdh_kem_public_key_t : public ec_key_t { private: Botan::ECDH_PublicKey botan_key_ecdh(rnp::RNG *rng) const; Botan::X25519_PublicKey botan_key_x25519() const; + Botan::X448_PublicKey botan_key_x448() const; std::vector key_; }; @@ -120,6 +123,7 @@ class ecdh_kem_private_key_t : public ec_key_t { private: Botan::ECDH_PrivateKey botan_key_ecdh(rnp::RNG *rng) const; Botan::X25519_PrivateKey botan_key_x25519() const; + Botan::X448_PrivateKey botan_key_x448() const; Botan::secure_vector key_; }; diff --git a/src/lib/crypto/kyber_ecdh_composite.cpp b/src/lib/crypto/kyber_ecdh_composite.cpp index fe170f6441..e4dec9ccb2 100644 --- a/src/lib/crypto/kyber_ecdh_composite.cpp +++ b/src/lib/crypto/kyber_ecdh_composite.cpp @@ -79,9 +79,8 @@ pgp_kyber_ecdh_composite_key_t::ecdh_curve_privkey_size(pgp_curve_t curve) switch (curve) { case PGP_CURVE_25519: return 32; - /* TODO */ - // case PGP_CURVE_X448: - // return 56; + case PGP_CURVE_448: + return 56; case PGP_CURVE_NIST_P_256: return 32; case PGP_CURVE_NIST_P_384: @@ -102,9 +101,8 @@ pgp_kyber_ecdh_composite_key_t::ecdh_curve_pubkey_size(pgp_curve_t curve) switch (curve) { case PGP_CURVE_25519: return 32; - /* TODO */ - // case PGP_CURVE_X448: - // return 56; + case PGP_CURVE_448: + return 56; case PGP_CURVE_NIST_P_256: return 65; case PGP_CURVE_NIST_P_384: @@ -125,9 +123,8 @@ pgp_kyber_ecdh_composite_key_t::ecdh_curve_ephemeral_size(pgp_curve_t curve) switch (curve) { case PGP_CURVE_25519: return 32; - /* TODO */ - // case PGP_CURVE_X448: - // return 56; + case PGP_CURVE_448: + return 56; case PGP_CURVE_NIST_P_256: return 65; case PGP_CURVE_NIST_P_384: @@ -148,9 +145,8 @@ pgp_kyber_ecdh_composite_key_t::ecdh_curve_keyshare_size(pgp_curve_t curve) switch (curve) { case PGP_CURVE_25519: return 32; - /* TODO */ - // case PGP_CURVE_X448: - // return 56; + case PGP_CURVE_448: + return 56; case PGP_CURVE_NIST_P_256: return 32; case PGP_CURVE_NIST_P_384: @@ -175,6 +171,8 @@ pgp_kyber_ecdh_composite_key_t::pk_alg_to_kyber_id(pgp_pubkey_alg_t pk_alg) FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_BP256: return kyber_768; + case PGP_PKA_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -199,8 +197,8 @@ pgp_kyber_ecdh_composite_key_t::pk_alg_to_curve_id(pgp_pubkey_alg_t pk_alg) return PGP_CURVE_BP384; case PGP_PKA_KYBER1024_P384: return PGP_CURVE_NIST_P_384; - /*case PGP_PKA_KYBER1024_X448: - return ... NOT_IMPLEMENTED*/ + case PGP_PKA_KYBER1024_X448: + return PGP_CURVE_448; default: RNP_LOG("invalid PK alg given"); throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); @@ -312,7 +310,7 @@ hashed_ecc_keyshare(const std::vector &key_share, case PGP_PKA_KYBER768_P256: hash_alg = PGP_HASH_SHA3_256; break; - // case PGP_PKA_KYBER1024_X448: + case PGP_PKA_KYBER1024_X448: case PGP_PKA_KYBER1024_P384: case PGP_PKA_KYBER1024_BP384: hash_alg = PGP_HASH_SHA3_512; diff --git a/src/lib/crypto/x25519.cpp b/src/lib/crypto/x25519.cpp index 9fb2c340f8..f3449c7515 100644 --- a/src/lib/crypto/x25519.cpp +++ b/src/lib/crypto/x25519.cpp @@ -26,22 +26,24 @@ #include "x25519.h" #include -#if defined(ENABLE_CRYPTO_REFRESH) +#include #include "exdsa_ecdhkem.h" #include "hkdf.hpp" #include "utils.h" #include "botan/rfc3394.h" +static const std::vector hkdf_x25519_info_str = { + 'O', 'p', 'e', 'n', 'P', 'G', 'P', ' ', 'X', '2', '5', '5', '1', '9'}; +static const std::vector hkdf_x448_info_str = { + 'O', 'p', 'e', 'n', 'P', 'G', 'P', ' ', 'X', '4', '4', '8'}; + static void -x25519_hkdf(std::vector & derived_key, - const std::vector &ephemeral_pubkey_material, - const std::vector &recipient_pubkey_material, - const std::vector &shared_key) +x_hkdf(std::vector & derived_key, + const std::vector &ephemeral_pubkey_material, + const std::vector &recipient_pubkey_material, + const std::vector &shared_key, + const std::vector &info_str) { - /* The shared secret is passed to HKDF (see {{RFC5869}}) using SHA256, and the - * UTF-8-encoded string "OpenPGP X25519" as the info parameter. */ - static const std::vector info = { - 'O', 'p', 'e', 'n', 'P', 'G', 'P', ' ', 'X', '2', '5', '5', '1', '9'}; auto kdf = rnp::Hkdf::create(PGP_HASH_SHA256); derived_key.resize(pgp_key_size(PGP_SA_AES_128)); // 128-bit AES key wrap @@ -58,8 +60,8 @@ x25519_hkdf(std::vector & derived_key, 0, // no salt kdf_input.data(), kdf_input.size(), - info.data(), - info.size(), + info_str.data(), + info_str.size(), derived_key.data(), derived_key.size()); } @@ -88,7 +90,7 @@ x25519_native_encrypt(rnp::RNG * rng, return ret; } - x25519_hkdf(derived_key, encrypted->eph_key, pubkey, shared_key); + x_hkdf(derived_key, encrypted->eph_key, pubkey, shared_key, hkdf_x25519_info_str); Botan::SymmetricKey kek(derived_key); try { @@ -132,7 +134,7 @@ x25519_native_decrypt(rnp::RNG * rng, return ret; } - x25519_hkdf(derived_key, encrypted->eph_key, keypair.pub, shared_key); + x_hkdf(derived_key, encrypted->eph_key, keypair.pub, shared_key, hkdf_x25519_info_str); Botan::SymmetricKey kek(derived_key); auto tmp_out = @@ -170,8 +172,6 @@ x25519_validate_key_native(rnp::RNG *rng, const pgp_x25519_key_t *key, bool secr return (valid_pub && valid_priv) ? RNP_SUCCESS : RNP_ERROR_BAD_PARAMETERS; } -#endif - rnp_result_t generate_x25519_native(rnp::RNG * rng, std::vector &privkey, @@ -183,3 +183,120 @@ generate_x25519_native(rnp::RNG * rng, return RNP_SUCCESS; } + +rnp_result_t +generate_x448_native(rnp::RNG * rng, + std::vector &privkey, + std::vector &pubkey) +{ + Botan::X448_PrivateKey priv_key(*(rng->obj())); + pubkey = priv_key.public_value(); + privkey = Botan::unlock(priv_key.raw_private_key_bits()); + return RNP_SUCCESS; +} + +rnp_result_t +x448_native_encrypt(rnp::RNG * rng, + const std::vector &pubkey, + const uint8_t * in, + size_t in_len, + pgp_x448_encrypted_t * encrypted) +{ + rnp_result_t ret; + std::vector shared_key; + std::vector derived_key; + + if (!in_len || (in_len % 8) != 0) { + RNP_LOG("incorrect size of in, AES key wrap requires a multiple of 8 bytes"); + return RNP_ERROR_BAD_FORMAT; + } + + /* encapsulation */ + ecdh_kem_public_key_t ecdhkem_pubkey(pubkey, PGP_CURVE_448); + ret = ecdhkem_pubkey.encapsulate(rng, encrypted->eph_key, shared_key); + if (ret != RNP_SUCCESS) { + RNP_LOG("encapsulation failed"); + return ret; + } + + x_hkdf(derived_key, encrypted->eph_key, pubkey, shared_key, hkdf_x448_info_str); + + Botan::SymmetricKey kek(derived_key); + try { + encrypted->enc_sess_key = Botan::unlock( + Botan::rfc3394_keywrap(Botan::secure_vector(in, in + in_len), kek)); + } catch (const std::exception &e) { + RNP_LOG("Keywrap failed: %s", e.what()); + return RNP_ERROR_ENCRYPT_FAILED; + } + + return RNP_SUCCESS; +} + +rnp_result_t +x448_native_decrypt(rnp::RNG * rng, + const pgp_x448_key_t & keypair, + const pgp_x448_encrypted_t *encrypted, + uint8_t * decbuf, + size_t * decbuf_len) +{ + rnp_result_t ret; + std::vector shared_key; + std::vector derived_key; + + static const size_t x448_pubkey_size = 32; + if (encrypted->eph_key.size() != x448_pubkey_size) { + RNP_LOG("Wrong ephemeral public key size"); + return RNP_ERROR_BAD_FORMAT; + } + if (!encrypted->enc_sess_key.size()) { + // TODO: could do a check for possible sizes + RNP_LOG("No encrypted session key provided"); + return RNP_ERROR_BAD_FORMAT; + } + + /* decapsulate */ + ecdh_kem_private_key_t ecdhkem_privkey(keypair.priv, PGP_CURVE_448); + ret = ecdhkem_privkey.decapsulate(rng, encrypted->eph_key, shared_key); + if (ret != RNP_SUCCESS) { + RNP_LOG("decapsulation failed"); + return ret; + } + + x_hkdf(derived_key, encrypted->eph_key, keypair.pub, shared_key, hkdf_x448_info_str); + + Botan::SymmetricKey kek(derived_key); + auto tmp_out = + Botan::rfc3394_keyunwrap(Botan::secure_vector(encrypted->enc_sess_key.begin(), + encrypted->enc_sess_key.end()), + kek); + if (*decbuf_len < tmp_out.size()) { + RNP_LOG("buffer for decryption result too small"); + return RNP_ERROR_DECRYPT_FAILED; + } + *decbuf_len = tmp_out.size(); + memcpy(decbuf, tmp_out.data(), tmp_out.size()); + + return RNP_SUCCESS; +} + +rnp_result_t +x448_validate_key_native(rnp::RNG *rng, const pgp_x448_key_t *key, bool secret) +{ + bool valid_pub; + bool valid_priv; + + Botan::X448_PublicKey pub_key(key->priv); + valid_pub = pub_key.check_key(*(rng->obj()), false); + + if (secret) { + Botan::X448_PrivateKey priv_key( + Botan::secure_vector(key->priv.begin(), key->priv.end())); + valid_priv = priv_key.check_key(*(rng->obj()), false); + } else { + valid_priv = true; + } + + // check key returns true for successful check + return (valid_pub && valid_priv) ? RNP_SUCCESS : RNP_ERROR_BAD_PARAMETERS; +} \ No newline at end of file diff --git a/src/lib/crypto/x25519.h b/src/lib/crypto/x25519.h index 36fcd5d173..e5e92a1525 100644 --- a/src/lib/crypto/x25519.h +++ b/src/lib/crypto/x25519.h @@ -38,7 +38,6 @@ rnp_result_t generate_x25519_native(rnp::RNG * rng, std::vector &privkey, std::vector &pubkey); -#if defined(ENABLE_CRYPTO_REFRESH) rnp_result_t x25519_native_encrypt(rnp::RNG * rng, const std::vector &pubkey, const uint8_t * in, @@ -55,5 +54,21 @@ rnp_result_t x25519_validate_key_native(rnp::RNG * rng, const pgp_x25519_key_t *key, bool secret); -#endif +rnp_result_t generate_x448_native(rnp::RNG * rng, + std::vector &privkey, + std::vector &pubkey); + +rnp_result_t x448_native_encrypt(rnp::RNG * rng, + const std::vector &pubkey, + const uint8_t * in, + size_t in_len, + pgp_x448_encrypted_t * encrypted); + +rnp_result_t x448_native_decrypt(rnp::RNG * rng, + const pgp_x448_key_t & keypair, + const pgp_x448_encrypted_t *encrypted, + uint8_t * decbuf, + size_t * decbuf_len); + +rnp_result_t x448_validate_key_native(rnp::RNG *rng, const pgp_x448_key_t *key, bool secret); #endif diff --git a/src/lib/generate-key.cpp b/src/lib/generate-key.cpp index 79c9d15d77..0c31f0f31a 100644 --- a/src/lib/generate-key.cpp +++ b/src/lib/generate-key.cpp @@ -60,16 +60,18 @@ static const id_str_pair pubkey_alg_map[] = { #if defined(ENABLE_CRYPTO_REFRESH) {PGP_PKA_ED25519, "ED25519"}, {PGP_PKA_X25519, "X25519"}, + {PGP_PKA_ED448, "ED448"}, + {PGP_PKA_X448, "X448"}, #endif #if defined(ENABLE_PQC) {PGP_PKA_KYBER768_X25519, "ML-KEM-768_X25519"}, - //{PGP_PKA_KYBER1024_X448, "Kyber-X448"}, + {PGP_PKA_KYBER1024_X448, "ML-KEM-1024_X448"}, {PGP_PKA_KYBER768_P256, "ML-KEM-768_P256"}, {PGP_PKA_KYBER1024_P384, "ML-KEM-1024_P384"}, {PGP_PKA_KYBER768_BP256, "ML-KEM-768_BP256"}, {PGP_PKA_KYBER1024_BP384, "ML-KEM-1024_BP384"}, {PGP_PKA_DILITHIUM3_ED25519, "ML-DSA-65_ED25519"}, - //{PGP_PKA_DILITHIUM5_ED448, "Dilithium-ED448"}, + {PGP_PKA_DILITHIUM5_ED448, "ML-DSA-87_ED448"}, {PGP_PKA_DILITHIUM3_P256, "ML-DSA-65_P256"}, {PGP_PKA_DILITHIUM5_P384, "ML-DSA-87_P384"}, {PGP_PKA_DILITHIUM3_BP256, "ML-DSA-65_BP256"}, @@ -82,8 +84,6 @@ static const id_str_pair pubkey_alg_map[] = { {PGP_PKA_PRIVATE02, "Private/Experimental"}, {PGP_PKA_PRIVATE03, "Private/Experimental"}, {PGP_PKA_PRIVATE04, "Private/Experimental"}, - {PGP_PKA_PRIVATE06, "Private/Experimental"}, - {PGP_PKA_PRIVATE08, "Private/Experimental"}, {PGP_PKA_PRIVATE10, "Private/Experimental"}, #else {PGP_PKA_PRIVATE00, "Private/Experimental"}, @@ -223,7 +223,8 @@ pgp_check_key_hash_requirements(const rnp_keygen_crypto_params_t &crypto) break; case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -356,6 +357,10 @@ get_numbits(const rnp_keygen_crypto_params_t *crypto) return 255; case PGP_PKA_X25519: return 255; + case PGP_PKA_ED448: + return 57 * 8; // TODO: sensible? + case PGP_PKA_X448: + return 56 * 8; // TODO: sensible? #endif case PGP_PKA_DSA: return crypto->dsa.p_bitlen; @@ -365,7 +370,8 @@ get_numbits(const rnp_keygen_crypto_params_t *crypto) #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: FALLTHROUGH_STATEMENT; - // TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; + case PGP_PKA_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -376,7 +382,8 @@ get_numbits(const rnp_keygen_crypto_params_t *crypto) return pgp_kyber_ecdh_composite_public_key_t::encoded_size(crypto->key_alg) * 8; case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: diff --git a/src/lib/key_material.cpp b/src/lib/key_material.cpp index 3241591f1b..037c52ccb5 100644 --- a/src/lib/key_material.cpp +++ b/src/lib/key_material.cpp @@ -415,7 +415,8 @@ KeyMaterial::create(pgp_pubkey_alg_t alg) #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: FALLTHROUGH_STATEMENT; - // TODO add case PGP_PKA_KYBER1024_X448 + case PGP_PKA_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -426,7 +427,8 @@ KeyMaterial::create(pgp_pubkey_alg_t alg) return std::unique_ptr(new MlkemEcdhKeyMaterial(alg)); case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448 + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -1650,6 +1652,260 @@ X25519KeyMaterial::priv() const noexcept { return key_.priv; } + +std::unique_ptr +Ed448KeyMaterial::clone() +{ + return std::unique_ptr(new Ed448KeyMaterial(*this)); +} + +void +Ed448KeyMaterial::grip_update(rnp::Hash &hash) const +{ + // TODO: if GnuPG would ever support v6, check whether this works correctly. + hash.add(pub()); +} + +bool +Ed448KeyMaterial::validate_material(rnp::SecurityContext &ctx, bool reset) +{ + return !ed448_validate_key_native(&ctx.rng, &key_, secret_); +} + +bool +Ed448KeyMaterial::equals(const KeyMaterial &value) const noexcept +{ + auto key = dynamic_cast(&value); + if (!key || !KeyMaterial::equals(value)) { + return false; + } + return key->key_.pub == key_.pub; +} + +void +Ed448KeyMaterial::clear_secret() noexcept +{ + key_.clear_secret(); + KeyMaterial::clear_secret(); +} + +bool +Ed448KeyMaterial::parse(pgp_packet_body_t &pkt) noexcept +{ + secret_ = false; + auto ec_desc = get_curve_desc(PGP_CURVE_ED448); + std::vector buf(BITS_TO_BYTES(ec_desc->bitlen)); + if (!pkt.get(buf.data(), buf.size())) { + RNP_LOG("failed to parse Ed448 public key data"); + return false; + } + key_.pub = buf; + return true; +} + +bool +Ed448KeyMaterial::parse_secret(pgp_packet_body_t &pkt) noexcept +{ + auto ec_desc = get_curve_desc(PGP_CURVE_ED448); + std::vector buf(BITS_TO_BYTES(ec_desc->bitlen)); + if (!pkt.get(buf.data(), buf.size())) { + RNP_LOG("failed to parse Ed448 secret key data"); + return false; + } + key_.priv = buf; + secret_ = true; + return true; +} + +void +Ed448KeyMaterial::write(pgp_packet_body_t &pkt) const +{ + pkt.add(key_.pub); +} + +void +Ed448KeyMaterial::write_secret(pgp_packet_body_t &pkt) const +{ + pkt.add(key_.priv); +} + +bool +Ed448KeyMaterial::generate(const rnp_keygen_crypto_params_t ¶ms) +{ + if (generate_ed448_native(¶ms.ctx->rng, key_.priv, key_.pub)) { + RNP_LOG("failed to generate ED448 key"); + return false; + } + return finish_generate(); +} + +rnp_result_t +Ed448KeyMaterial::verify(const rnp::SecurityContext & ctx, + const pgp_signature_material_t & sig, + const rnp::secure_vector &hash) const +{ + return ed448_verify_native(sig.ed448.sig, key_.pub, hash.data(), hash.size()); +} + +rnp_result_t +Ed448KeyMaterial::sign(rnp::SecurityContext & ctx, + pgp_signature_material_t & sig, + const rnp::secure_vector &hash) const +{ + return ed448_sign_native(&ctx.rng, sig.ed448.sig, key_.priv, hash.data(), hash.size()); +} + +size_t +Ed448KeyMaterial::bits() const noexcept +{ + return 57 * 8; // TODO: sensible? +} + +pgp_curve_t +Ed448KeyMaterial::curve() const noexcept +{ + return PGP_CURVE_ED448; +} + +const std::vector & +Ed448KeyMaterial::pub() const noexcept +{ + return key_.pub; +} + +const std::vector & +Ed448KeyMaterial::priv() const noexcept +{ + return key_.priv; +} + +std::unique_ptr +X448KeyMaterial::clone() +{ + return std::unique_ptr(new X448KeyMaterial(*this)); +} + +void +X448KeyMaterial::grip_update(rnp::Hash &hash) const +{ + // TODO: if GnuPG would ever support v6, check whether this works correctly. + hash.add(pub()); +} + +bool +X448KeyMaterial::validate_material(rnp::SecurityContext &ctx, bool reset) +{ + return !x448_validate_key_native(&ctx.rng, &key_, secret_); +} + +bool +X448KeyMaterial::equals(const KeyMaterial &value) const noexcept +{ + auto key = dynamic_cast(&value); + if (!key || !KeyMaterial::equals(value)) { + return false; + } + return key->key_.pub == key_.pub; +} + +void +X448KeyMaterial::clear_secret() noexcept +{ + key_.clear_secret(); + KeyMaterial::clear_secret(); +} + +bool +X448KeyMaterial::parse(pgp_packet_body_t &pkt) noexcept +{ + secret_ = false; + auto ec_desc = get_curve_desc(PGP_CURVE_448); + std::vector buf(BITS_TO_BYTES(ec_desc->bitlen)); + if (!pkt.get(buf.data(), buf.size())) { + RNP_LOG("failed to parse X448 public key data"); + return false; + } + key_.pub = buf; + return true; +} + +bool +X448KeyMaterial::parse_secret(pgp_packet_body_t &pkt) noexcept +{ + auto ec_desc = get_curve_desc(PGP_CURVE_448); + std::vector buf(BITS_TO_BYTES(ec_desc->bitlen)); + if (!pkt.get(buf.data(), buf.size())) { + RNP_LOG("failed to parse X448 secret key data"); + return false; + } + key_.priv = buf; + secret_ = true; + return true; +} + +void +X448KeyMaterial::write(pgp_packet_body_t &pkt) const +{ + pkt.add(key_.pub); +} + +void +X448KeyMaterial::write_secret(pgp_packet_body_t &pkt) const +{ + pkt.add(key_.priv); +} + +bool +X448KeyMaterial::generate(const rnp_keygen_crypto_params_t ¶ms) +{ + if (generate_x448_native(¶ms.ctx->rng, key_.priv, key_.pub)) { + RNP_LOG("failed to generate X448 key"); + return false; + } + return finish_generate(); +} + +rnp_result_t +X448KeyMaterial::encrypt(rnp::SecurityContext & ctx, + pgp_encrypted_material_t &out, + const uint8_t * data, + size_t len) const +{ + return x448_native_encrypt(&ctx.rng, key_.pub, data, len, &out.x448); +} + +rnp_result_t +X448KeyMaterial::decrypt(rnp::SecurityContext & ctx, + uint8_t * out, + size_t & out_len, + const pgp_encrypted_material_t &in) const +{ + return x448_native_decrypt(&ctx.rng, key_, &in.x448, out, &out_len); +} + +size_t +X448KeyMaterial::bits() const noexcept +{ + return 56 * 8; // TODO: sensible? +} + +pgp_curve_t +X448KeyMaterial::curve() const noexcept +{ + return PGP_CURVE_448; +} + +const std::vector & +X448KeyMaterial::pub() const noexcept +{ + return key_.pub; +} + +const std::vector & +X448KeyMaterial::priv() const noexcept +{ + return key_.priv; +} #endif #if defined(ENABLE_PQC) diff --git a/src/lib/key_material.hpp b/src/lib/key_material.hpp index 06f244f5c7..fe4a653cf2 100644 --- a/src/lib/key_material.hpp +++ b/src/lib/key_material.hpp @@ -532,6 +532,70 @@ class X25519KeyMaterial : public KeyMaterial { const std::vector &pub() const noexcept; const std::vector &priv() const noexcept; }; + +class Ed448KeyMaterial : public KeyMaterial { + pgp_ed448_key_t key_; + + protected: + void grip_update(rnp::Hash &hash) const override; + bool validate_material(rnp::SecurityContext &ctx, bool reset) override; + + public: + Ed448KeyMaterial() : KeyMaterial(PGP_PKA_ED448), key_{} {}; + std::unique_ptr clone() override; + + bool equals(const KeyMaterial &value) const noexcept override; + void clear_secret() noexcept override; + bool parse(pgp_packet_body_t &pkt) noexcept override; + bool parse_secret(pgp_packet_body_t &pkt) noexcept override; + void write(pgp_packet_body_t &pkt) const override; + void write_secret(pgp_packet_body_t &pkt) const override; + bool generate(const rnp_keygen_crypto_params_t ¶ms) override; + rnp_result_t verify(const rnp::SecurityContext & ctx, + const pgp_signature_material_t & sig, + const rnp::secure_vector &hash) const override; + rnp_result_t sign(rnp::SecurityContext & ctx, + pgp_signature_material_t & sig, + const rnp::secure_vector &hash) const override; + size_t bits() const noexcept override; + pgp_curve_t curve() const noexcept override; + + const std::vector &pub() const noexcept; + const std::vector &priv() const noexcept; +}; + +class X448KeyMaterial : public KeyMaterial { + pgp_x448_key_t key_; + + protected: + void grip_update(rnp::Hash &hash) const override; + bool validate_material(rnp::SecurityContext &ctx, bool reset) override; + + public: + X448KeyMaterial() : KeyMaterial(PGP_PKA_X448), key_{} {}; + std::unique_ptr clone() override; + + bool equals(const KeyMaterial &value) const noexcept override; + void clear_secret() noexcept override; + bool parse(pgp_packet_body_t &pkt) noexcept override; + bool parse_secret(pgp_packet_body_t &pkt) noexcept override; + void write(pgp_packet_body_t &pkt) const override; + void write_secret(pgp_packet_body_t &pkt) const override; + bool generate(const rnp_keygen_crypto_params_t ¶ms) override; + rnp_result_t encrypt(rnp::SecurityContext & ctx, + pgp_encrypted_material_t &out, + const uint8_t * data, + size_t len) const override; + rnp_result_t decrypt(rnp::SecurityContext & ctx, + uint8_t * out, + size_t & out_len, + const pgp_encrypted_material_t &in) const override; + size_t bits() const noexcept override; + pgp_curve_t curve() const noexcept override; + + const std::vector &pub() const noexcept; + const std::vector &priv() const noexcept; +}; #endif #if defined(ENABLE_PQC) diff --git a/src/lib/pgp-key.cpp b/src/lib/pgp-key.cpp index 0bb3239da7..a01e498b8f 100644 --- a/src/lib/pgp-key.cpp +++ b/src/lib/pgp-key.cpp @@ -160,8 +160,10 @@ pgp_pk_alg_capabilities(pgp_pubkey_alg_t alg) #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: + case PGP_PKA_ED448: return pgp_key_flags_t(PGP_KF_SIGN | PGP_KF_CERTIFY | PGP_KF_AUTH); case PGP_PKA_X25519: + case PGP_PKA_X448: return PGP_KF_ENCRYPT; #endif @@ -175,7 +177,8 @@ pgp_pk_alg_capabilities(pgp_pubkey_alg_t alg) #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: FALLTHROUGH_STATEMENT; - // TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; + case PGP_PKA_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -187,7 +190,8 @@ pgp_pk_alg_capabilities(pgp_pubkey_alg_t alg) case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: diff --git a/src/lib/rnp.cpp b/src/lib/rnp.cpp index 1d9f3a480d..1cbfa6a32b 100644 --- a/src/lib/rnp.cpp +++ b/src/lib/rnp.cpp @@ -166,16 +166,18 @@ static const id_str_pair pubkey_alg_map[] = { #if defined(ENABLE_CRYPTO_REFRESH) {PGP_PKA_ED25519, RNP_ALGNAME_ED25519}, {PGP_PKA_X25519, RNP_ALGNAME_X25519}, + {PGP_PKA_ED448, RNP_ALGNAME_ED448}, + {PGP_PKA_X448, RNP_ALGNAME_X448}, #endif #if defined(ENABLE_PQC) {PGP_PKA_KYBER768_X25519, RNP_ALGNAME_KYBER768_X25519}, - //{PGP_PKA_KYBER1024_X448, RNP_ALGNAME_KYBER1024_X448}, + {PGP_PKA_KYBER1024_X448, RNP_ALGNAME_KYBER1024_X448}, {PGP_PKA_KYBER768_P256, RNP_ALGNAME_KYBER768_P256}, {PGP_PKA_KYBER1024_P384, RNP_ALGNAME_KYBER1024_P384}, {PGP_PKA_KYBER768_BP256, RNP_ALGNAME_KYBER768_BP256}, {PGP_PKA_KYBER1024_BP384, RNP_ALGNAME_KYBER1024_BP384}, {PGP_PKA_DILITHIUM3_ED25519, RNP_ALGNAME_DILITHIUM3_ED25519}, - //{PGP_PKA_DILITHIUM5_ED448, RNP_ALGNAME_DILITHIUM5_ED448}, + {PGP_PKA_DILITHIUM5_ED448, RNP_ALGNAME_DILITHIUM5_ED448}, {PGP_PKA_DILITHIUM3_P256, RNP_ALGNAME_DILITHIUM3_P256}, {PGP_PKA_DILITHIUM5_P384, RNP_ALGNAME_DILITHIUM5_P384}, {PGP_PKA_DILITHIUM3_BP256, RNP_ALGNAME_DILITHIUM3_BP256}, @@ -340,17 +342,19 @@ pub_alg_supported(int alg) #endif #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_X25519: + case PGP_PKA_X448: case PGP_PKA_ED25519: + case PGP_PKA_ED448: #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: - // case PGP_PKA_KYBER1024_X448: + case PGP_PKA_KYBER1024_X448: case PGP_PKA_KYBER768_P256: case PGP_PKA_KYBER1024_P384: case PGP_PKA_KYBER768_BP256: case PGP_PKA_KYBER1024_BP384: case PGP_PKA_DILITHIUM3_ED25519: - // case PGP_PKA_DILITHIUM5_ED448: + case PGP_PKA_DILITHIUM5_ED448: case PGP_PKA_DILITHIUM3_P256: case PGP_PKA_DILITHIUM5_P384: case PGP_PKA_DILITHIUM3_BP256: @@ -5032,14 +5036,17 @@ default_key_flags(pgp_pubkey_alg_t alg, bool subkey) return PGP_KF_ENCRYPT; #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: + case PGP_PKA_ED448: return subkey ? PGP_KF_SIGN : pgp_key_flags_t(PGP_KF_SIGN | PGP_KF_CERTIFY); case PGP_PKA_X25519: + case PGP_PKA_X448: return PGP_KF_ENCRYPT; #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: FALLTHROUGH_STATEMENT; - // TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; + case PGP_PKA_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -5050,7 +5057,8 @@ default_key_flags(pgp_pubkey_alg_t alg, bool subkey) return PGP_KF_ENCRYPT; case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -8045,12 +8053,15 @@ add_json_mpis(json_object *jso, pgp_key_t *key, bool secret = false) #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: case PGP_PKA_X25519: + case PGP_PKA_ED448: + case PGP_PKA_X448: return RNP_SUCCESS; /* TODO */ #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: FALLTHROUGH_STATEMENT; - // TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; + case PGP_PKA_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -8061,7 +8072,8 @@ add_json_mpis(json_object *jso, pgp_key_t *key, bool secret = false) return RNP_SUCCESS; /* TODO */ case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -8112,12 +8124,15 @@ add_json_sig_mpis(json_object *jso, const pgp_signature_t *sig) #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: case PGP_PKA_X25519: + case PGP_PKA_ED448: + case PGP_PKA_X448: return RNP_SUCCESS; /* TODO */ #endif #if defined(ENABLE_PQC) case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -8344,12 +8359,15 @@ key_to_json(json_object *jso, rnp_key_handle_t handle, uint32_t flags) #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: case PGP_PKA_X25519: + case PGP_PKA_ED448: + case PGP_PKA_X448: return RNP_SUCCESS; /* TODO */ #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: FALLTHROUGH_STATEMENT; - // TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; + case PGP_PKA_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -8360,7 +8378,8 @@ key_to_json(json_object *jso, rnp_key_handle_t handle, uint32_t flags) return RNP_SUCCESS; /* TODO */ case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: diff --git a/src/lib/types.h b/src/lib/types.h index 2b90f8d0aa..7f4879cd61 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -177,6 +177,7 @@ typedef struct pgp_signature_material_t { }; #if defined(ENABLE_CRYPTO_REFRESH) pgp_ed25519_signature_t ed25519; // non-trivial type cannot be member in union + pgp_ed448_signature_t ed448; // non-trivial type cannot be member in union #endif #if defined(ENABLE_PQC) pgp_dilithium_exdsa_signature_t @@ -198,6 +199,7 @@ typedef struct pgp_encrypted_material_t { }; #if defined(ENABLE_CRYPTO_REFRESH) pgp_x25519_encrypted_t x25519; // non-trivial type cannot be member in union + pgp_x448_encrypted_t x448; // non-trivial type cannot be member in union #endif #if defined(ENABLE_PQC) pgp_kyber_ecdh_encrypted_t kyber_ecdh; // non-trivial type cannot be member in union diff --git a/src/librepgp/stream-common.cpp b/src/librepgp/stream-common.cpp index c7ba9326dc..dc8221f7ce 100644 --- a/src/librepgp/stream-common.cpp +++ b/src/librepgp/stream-common.cpp @@ -1196,10 +1196,11 @@ have_pkesk_checksum(pgp_pubkey_alg_t alg) switch (alg) { #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_X25519: + case PGP_PKA_X448: #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: - // case PGP_PKA_KYBER1024_X448: + case PGP_PKA_KYBER1024_X448: case PGP_PKA_KYBER768_P256: case PGP_PKA_KYBER1024_P384: case PGP_PKA_KYBER768_BP256: diff --git a/src/librepgp/stream-dump.cpp b/src/librepgp/stream-dump.cpp index 8219d0eda8..2a9951acdf 100644 --- a/src/librepgp/stream-dump.cpp +++ b/src/librepgp/stream-dump.cpp @@ -145,16 +145,18 @@ static const id_str_pair pubkey_alg_map[] = { #if defined(ENABLE_CRYPTO_REFRESH) {PGP_PKA_ED25519, "Ed25519"}, {PGP_PKA_X25519, "X25519"}, + {PGP_PKA_ED448, "Ed448"}, + {PGP_PKA_X448, "X448"}, #endif #if defined(ENABLE_PQC) {PGP_PKA_KYBER768_X25519, "ML-KEM-768 + X25519"}, - //{PGP_PKA_KYBER1024_X448, "Kyber1024 + X448"}, + {PGP_PKA_KYBER1024_X448, "ML-KEM-1024 + X448"}, {PGP_PKA_KYBER768_P256, "ML-KEM-768 + NIST P-256"}, {PGP_PKA_KYBER1024_P384, "ML-KEM-1024 + NIST P-384"}, {PGP_PKA_KYBER768_BP256, "ML-KEM-768 + Brainpool256"}, {PGP_PKA_KYBER1024_BP384, "ML-KEM-1024 + Brainpool384"}, {PGP_PKA_DILITHIUM3_ED25519, "ML-DSA-65 + ED25519"}, - //{PGP_PKA_DILITHIUM5_ED448, "Dilithium + X448"}, + {PGP_PKA_DILITHIUM5_ED448, "ML-DSA-87 + X448"}, {PGP_PKA_DILITHIUM3_P256, "ML-DSA-65 + NIST P-256"}, {PGP_PKA_DILITHIUM5_P384, "ML-DSA-87 + NIST P-384"}, {PGP_PKA_DILITHIUM3_BP256, "ML-DSA-65 + Brainpool256"}, @@ -852,11 +854,15 @@ stream_dump_signature_pkt(rnp_dump_ctx_t *ctx, pgp_signature_t *sig, pgp_dest_t case PGP_PKA_ED25519: dst_print_vec(dst, "ed25519 sig", material.ed25519.sig, ctx->dump_mpi); break; + case PGP_PKA_ED448: + dst_print_vec(dst, "ed448 sig", material.ed448.sig, ctx->dump_mpi); + break; #endif #if defined(ENABLE_PQC) case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -971,11 +977,22 @@ stream_dump_key_material(rnp_dump_ctx_t & ctx, dst_print_vec(dst, "x25519", x25519.pub(), ctx.dump_mpi); return; } + case PGP_PKA_ED448: { + auto &ed448 = dynamic_cast(*material); + dst_print_vec(dst, "ed448", ed448.pub(), ctx.dump_mpi); + return; + } + case PGP_PKA_X448: { + auto &x448 = dynamic_cast(*material); + dst_print_vec(dst, "x448", x448.pub(), ctx.dump_mpi); + return; + } #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: FALLTHROUGH_STATEMENT; - // TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; + case PGP_PKA_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -990,7 +1007,8 @@ stream_dump_key_material(rnp_dump_ctx_t & ctx, } case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -1232,7 +1250,8 @@ stream_dump_pk_session_key(rnp_dump_ctx_t *ctx, pgp_source_t *src, pgp_dest_t *d #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: FALLTHROUGH_STATEMENT; - // TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; + case PGP_PKA_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -2070,13 +2089,15 @@ stream_dump_signature_pkt_json(rnp_dump_ctx_t * ctx, break; #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: + case PGP_PKA_ED448: /* TODO */ break; #endif #if defined(ENABLE_PQC) case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -2188,13 +2209,16 @@ stream_dump_key_material_json(rnp_dump_ctx_t & ctx, #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: case PGP_PKA_X25519: + case PGP_PKA_ED448: + case PGP_PKA_X448: /* TODO */ return true; #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: FALLTHROUGH_STATEMENT; - // TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; + case PGP_PKA_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -2206,7 +2230,8 @@ stream_dump_key_material_json(rnp_dump_ctx_t & ctx, return true; case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -2412,13 +2437,16 @@ stream_dump_pk_session_key_json(rnp_dump_ctx_t *ctx, pgp_source_t *src, json_obj #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: case PGP_PKA_X25519: + case PGP_PKA_ED448: + case PGP_PKA_X448: /* TODO */ 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_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: diff --git a/src/librepgp/stream-packet.cpp b/src/librepgp/stream-packet.cpp index 9419436ea7..aa12e4447f 100644 --- a/src/librepgp/stream-packet.cpp +++ b/src/librepgp/stream-packet.cpp @@ -1272,11 +1272,41 @@ pgp_pk_sesskey_t::parse_material(pgp_encrypted_material_t &material) } break; } + case PGP_PKA_X448: { + uint8_t bt = 0; + const ec_curve_desc_t *ec_desc = get_curve_desc(PGP_CURVE_448); + material.x448.eph_key.resize(BITS_TO_BYTES(ec_desc->bitlen)); + if (!pkt.get(material.x448.eph_key.data(), material.x448.eph_key.size())) { + RNP_LOG("failed to parse X448 PKESK (eph. pubkey)"); + return false; + } + uint8_t enc_sesskey_len; + if (!pkt.get(enc_sesskey_len)) { + RNP_LOG("failed to parse X448 PKESK (enc sesskey length)"); + return false; + } + /* get plaintext salg if PKESKv3 */ + if ((version == PGP_PKSK_V3) && !do_encrypt_pkesk_v3_alg_id(alg)) { + if (!pkt.get(bt)) { + RNP_LOG("failed to get salg"); + return RNP_ERROR_BAD_FORMAT; + } + enc_sesskey_len -= 1; + salg = (pgp_symm_alg_t) bt; + } + material.x448.enc_sess_key.resize(enc_sesskey_len); + if (!pkt.get(material.x448.enc_sess_key.data(), enc_sesskey_len)) { + RNP_LOG("failed to parse X448 PKESK (enc sesskey)"); + 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_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -1361,11 +1391,23 @@ pgp_pk_sesskey_t::write_material(const pgp_encrypted_material_t &material) pktbody.add(material.x25519.enc_sess_key); break; } + case PGP_PKA_X448: { + uint8_t enc_sesskey_length_offset = ((version == PGP_PKSK_V3) ? 1 : 0); + pktbody.add(material.x448.eph_key); + pktbody.add_byte( + static_cast(material.x448.enc_sess_key.size() + enc_sesskey_length_offset)); + if (version == PGP_PKSK_V3) { + pktbody.add_byte(salg); /* added as plaintext */ + } + pktbody.add(material.x448.enc_sess_key); + 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_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: diff --git a/src/librepgp/stream-sig.cpp b/src/librepgp/stream-sig.cpp index 4f8beaed31..22daab750f 100644 --- a/src/librepgp/stream-sig.cpp +++ b/src/librepgp/stream-sig.cpp @@ -1073,7 +1073,7 @@ pgp_signature_t::parse_material(pgp_signature_material_t &material) const break; #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: { - const ec_curve_desc_t *ec_desc = get_curve_desc(PGP_CURVE_25519); + const ec_curve_desc_t *ec_desc = get_curve_desc(PGP_CURVE_ED25519); material.ed25519.sig.resize(2 * BITS_TO_BYTES(ec_desc->bitlen)); if (!pkt.get(material.ed25519.sig.data(), material.ed25519.sig.size())) { RNP_LOG("failed to parse ED25519 signature data"); @@ -1081,11 +1081,21 @@ pgp_signature_t::parse_material(pgp_signature_material_t &material) const } break; } + case PGP_PKA_ED448: { + const ec_curve_desc_t *ec_desc = get_curve_desc(PGP_CURVE_ED448); + material.ed448.sig.resize(2 * BITS_TO_BYTES(ec_desc->bitlen)); + if (!pkt.get(material.ed448.sig.data(), material.ed448.sig.size())) { + RNP_LOG("failed to parse ED448 signature data"); + return false; + } + break; + } #endif #if defined(ENABLE_PQC) case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -1198,11 +1208,15 @@ pgp_signature_t::write_material(const pgp_signature_material_t &material) case PGP_PKA_ED25519: pktbody.add(material.ed25519.sig); break; + case PGP_PKA_ED448: + pktbody.add(material.ed448.sig); + break; #endif #if defined(ENABLE_PQC) case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: diff --git a/src/rnpkeys/tui.cpp b/src/rnpkeys/tui.cpp index 5ee7077d8a..1b24f1b9f8 100644 --- a/src/rnpkeys/tui.cpp +++ b/src/rnpkeys/tui.cpp @@ -251,6 +251,7 @@ rnpkeys_ask_generate_params(rnp_cfg &cfg, FILE *input_fp) "\t(24) Ed25519Legacy + Curve25519Legacy + (ML-KEM-768 + X25519)\n" #if defined(ENABLE_CRYPTO_REFRESH) // PQC primary keys only for v6 "\t(25) (ML-DSA-65 + Ed25519) + (ML-KEM-768 + X25519)\n" + "\t(26) (ML-DSA-87 + Ed448) + (ML-KEM-1024 + X448)\n" "\t(27) (ML-DSA-65 + ECDSA-NIST-P-256) + (ML-KEM-768 + ECDH-NIST-P-256)\n" "\t(28) (ML-DSA-87 + ECDSA-NIST-P-384) + (ML-KEM-1024 + ECDH-NIST-P-384)\n" "\t(29) (ML-DSA-65 + ECDSA-brainpoolP256r1) + (ML-KEM-768 + ECDH-brainpoolP256r1)\n" @@ -345,6 +346,12 @@ rnpkeys_ask_generate_params(rnp_cfg &cfg, FILE *input_fp) cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER768_X25519); cfg.set_str(CFG_KG_V6_KEY, "true"); break; + case 26: + cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_DILITHIUM5_ED448); + cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_256); + cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER1024_X448); + cfg.set_str(CFG_KG_V6_KEY, "true"); + break; case 27: cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_DILITHIUM3_P256); cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_256); diff --git a/src/tests/cipher.cpp b/src/tests/cipher.cpp index 936daebfe0..14e570738a 100644 --- a/src/tests/cipher.cpp +++ b/src/tests/cipher.cpp @@ -568,7 +568,7 @@ TEST_F(rnp_tests, test_dsa_verify_negative) TEST_F(rnp_tests, kyber_ecdh_roundtrip) { pgp_pubkey_alg_t algs[] = {PGP_PKA_KYBER768_X25519, - /* PGP_PKA_KYBER1024_X448, */ // X448 not yet implemented + PGP_PKA_KYBER1024_X448, PGP_PKA_KYBER1024_P384, PGP_PKA_KYBER768_BP256, PGP_PKA_KYBER1024_BP384}; @@ -604,7 +604,8 @@ TEST_F(rnp_tests, dilithium_exdsa_signverify_success) const pgp_hash_alg_t hash_alg = PGP_HASH_SHA512; pgp_pubkey_alg_t algs[] = {PGP_PKA_DILITHIUM3_ED25519, - /* PGP_PKA_DILITHIUM5_ED448,*/ PGP_PKA_DILITHIUM3_P256, + PGP_PKA_DILITHIUM5_ED448, + PGP_PKA_DILITHIUM3_P256, PGP_PKA_DILITHIUM5_P384, PGP_PKA_DILITHIUM3_BP256, PGP_PKA_DILITHIUM5_BP384}; diff --git a/src/tests/cli_tests.py b/src/tests/cli_tests.py index 0d134c9964..a3829d988b 100755 --- a/src/tests/cli_tests.py +++ b/src/tests/cli_tests.py @@ -4648,6 +4648,7 @@ def test_encryption_and_signing_pqc(self): os.mkdir(RNPDIR_PQC, 0o700) algo_ui_exp_strs = [ "(24) Ed25519Legacy + Curve25519Legacy + (ML-KEM-768 + X25519)", "(25) (ML-DSA-65 + Ed25519) + (ML-KEM-768 + X25519)", + "(26) (ML-DSA-87 + Ed448) + (ML-KEM-1024 + X448)", "(27) (ML-DSA-65 + ECDSA-NIST-P-256) + (ML-KEM-768 + ECDH-NIST-P-256)", "(28) (ML-DSA-87 + ECDSA-NIST-P-384) + (ML-KEM-1024 + ECDH-NIST-P-384)", "(29) (ML-DSA-65 + ECDSA-brainpoolP256r1) + (ML-KEM-768 + ECDH-brainpoolP256r1)", @@ -4656,12 +4657,12 @@ def test_encryption_and_signing_pqc(self): "(32) SLH-DSA-SHAKE-128s + (ML-KEM-768 + X25519)", "(33) SLH-DSA-SHAKE-256s + (ML-KEM-1024 + ECDH-NIST-P-384)", ] - USERIDS = ['enc-sign25@rnp', 'enc-sign27@rnp', 'enc-sign28@rnp', 'enc-sign29@rnp', 'enc-sign30@rnp','enc-sign32a@rnp','enc-sign32b@rnp','enc-sign32c@rnp','enc-sign24-v4-key@rnp'] + USERIDS = ['enc-sign25@rnp', 'enc-sign26@rnp', 'enc-sign27@rnp', 'enc-sign28@rnp', 'enc-sign29@rnp', 'enc-sign30@rnp','enc-sign32a@rnp','enc-sign32b@rnp','enc-sign32c@rnp','enc-sign24-v4-key@rnp'] # '24' in the below array creates a v4 primary signature key with a v4 pqc subkey without a Features Subpacket. This way we test PQC encryption to a v4 subkey. RNP prefers the PQC subkey in case of a certificate having a PQC and a # non-PQC subkey. - ALGO = [25, 27, 28, 29, 30, 31, 32, 33, 24, ] - ALGO_PARAM = [None, None, None, None, None, None, None, None, None, ] + ALGO = [25, 26, 27, 28, 29, 30, 31, 32, 33, 24, ] + ALGO_PARAM = [None, None, None, None, None, None, None, None, None, None ] aead_list = [] passwds = [ ] for x in range(len(ALGO)): passwds.append('testpw' if x % 1 == 0 else '') diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp index 199d29067b..4889834ba1 100644 --- a/src/tests/ffi-enc.cpp +++ b/src/tests/ffi-enc.cpp @@ -915,6 +915,7 @@ TEST_F(rnp_tests, test_ffi_decrypt_v6_pkesk_test_vector) #if defined(ENABLE_PQC) TEST_F(rnp_tests, test_ffi_decrypt_pqc_pkesk_test_vector) { + return; // TODO: need new test vectors rnp_ffi_t ffi = NULL; rnp_input_t input = NULL; rnp_output_t output = NULL; diff --git a/src/tests/ffi-key.cpp b/src/tests/ffi-key.cpp index 5525efe588..4b51a2437f 100644 --- a/src/tests/ffi-key.cpp +++ b/src/tests/ffi-key.cpp @@ -3252,6 +3252,8 @@ TEST_F(rnp_tests, test_ffi_v6_cert_import) #if defined(ENABLE_PQC) TEST_F(rnp_tests, test_ffi_pqc_certs) { + return; // TODO: need new test vectors + rnp_ffi_t ffi = NULL; rnp_input_t input = NULL; size_t keycount = 255; diff --git a/src/tests/ffi.cpp b/src/tests/ffi.cpp index 377a4873f5..7ae2a0e396 100644 --- a/src/tests/ffi.cpp +++ b/src/tests/ffi.cpp @@ -3106,10 +3106,10 @@ TEST_F(rnp_tests, test_ffi_supported_features) size_t pqc_opt = 0; size_t crypto_refresh_opt = 0; #if defined(ENABLE_CRYPTO_REFRESH) - crypto_refresh_opt = 2; // X25519 + ED25519 + crypto_refresh_opt = 4; // Ed25519/X25519/Ed448/X448 #endif #if defined(ENABLE_PQC) - pqc_opt = 13; // kyber+ecc and dilithium+ecc and sphincs+ variants + pqc_opt = 6 + 6 + 3; // kyber+ecc + dilithium+ecc + sphincs+ variants #endif assert_true(check_features( RNP_FEATURE_PK_ALG, features, 6 + has_sm2 + pqc_opt + crypto_refresh_opt)); @@ -3212,7 +3212,12 @@ TEST_F(rnp_tests, test_ffi_supported_features) /* elliptic curve */ assert_rnp_success(rnp_supported_features(RNP_FEATURE_CURVE, &features)); assert_non_null(features); - assert_true(check_features(RNP_FEATURE_CURVE, features, 6 + has_sm2 + 3 * has_brainpool)); + size_t opt_has_ed448_x448 = 0; +#if defined(ENABLE_CRYPTO_REFRESH) + opt_has_ed448_x448 = 2; +#endif + assert_true(check_features( + RNP_FEATURE_CURVE, features, 6 + has_sm2 + 3 * has_brainpool + opt_has_ed448_x448)); rnp_buffer_destroy(features); assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "NIST P-256", &supported)); assert_true(supported); diff --git a/src/tests/pqc.cpp b/src/tests/pqc.cpp index 1f62b62fed..dfee4b2d87 100644 --- a/src/tests/pqc.cpp +++ b/src/tests/pqc.cpp @@ -88,7 +88,8 @@ TEST_F(rnp_tests, test_sphincsplus_key_function) TEST_F(rnp_tests, test_dilithium_exdsa_direct) { pgp_pubkey_alg_t algs[] = {PGP_PKA_DILITHIUM3_ED25519, - /* PGP_PKA_DILITHIUM5_ED448,*/ PGP_PKA_DILITHIUM3_P256, + PGP_PKA_DILITHIUM5_ED448, + PGP_PKA_DILITHIUM3_P256, PGP_PKA_DILITHIUM5_P384, PGP_PKA_DILITHIUM3_BP256, PGP_PKA_DILITHIUM5_BP384}; From 6828799543310c77e2217b45a9406c2daf59f762 Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Thu, 17 Oct 2024 15:50:40 +0200 Subject: [PATCH 07/15] make parsing PKESK key length more clear --- src/librepgp/stream-parse.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/librepgp/stream-parse.cpp b/src/librepgp/stream-parse.cpp index 6569d1d510..75df9b2c03 100644 --- a/src/librepgp/stream-parse.cpp +++ b/src/librepgp/stream-parse.cpp @@ -1633,14 +1633,17 @@ encrypted_try_key(pgp_source_encrypted_param_t *param, uint8_t *decbuf_sesskey = decbuf.data(); size_t decbuf_sesskey_len = declen; + size_t keylen; #if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) if (do_encrypt_pkesk_v3_alg_id(sesskey.alg)) #endif { sesskey.salg = static_cast(decbuf[0]); } - size_t keylen = pgp_key_size(sesskey.salg); if (sesskey.version == PGP_PKSK_V3) { + /* salg always set in this case, either from parsing the packet (plaintext salg) or + * from parsing the decrypted content (encrypted salg) */ + keylen = pgp_key_size(sesskey.salg); /* Check algorithm and key length */ if (!pgp_is_sa_supported(sesskey.salg)) { RNP_LOG("Unsupported symmetric algorithm %d", (int) sesskey.salg); @@ -1665,7 +1668,8 @@ encrypted_try_key(pgp_source_encrypted_param_t *param, } #if defined(ENABLE_CRYPTO_REFRESH) else { // V6 PKESK - /* compute the expected key length from the decbuf_sesskey_len and check */ + /* compute the expected key length from the decbuf_sesskey_len and check if it matches + * SEIPDv2 header */ keylen = have_pkesk_checksum(sesskey.alg) ? decbuf_sesskey_len - 2 : decbuf_sesskey_len; if (pgp_key_size(param->aead_hdr.ealg) != keylen) { From 4fa2d0c54514992420a378d2e8ad0314c0152ae3 Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Mon, 21 Oct 2024 10:41:44 +0200 Subject: [PATCH 08/15] update order in KEM combiner: PQ first --- src/lib/crypto/kmac.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/crypto/kmac.cpp b/src/lib/crypto/kmac.cpp index 4a8470ab37..3474de4e6e 100644 --- a/src/lib/crypto/kmac.cpp +++ b/src/lib/crypto/kmac.cpp @@ -75,10 +75,10 @@ KMAC256::Input_X(const std::vector &ecc_ciphertext, RNP_LOG_U8VEC(" - mlkemPublicKey: %s", kyber_pub); RNP_LOG(" - algId : %d", alg_id); #endif - res.insert(res.end(), ecc_ciphertext.begin(), ecc_ciphertext.end()); - res.insert(res.end(), ecc_pub.begin(), ecc_pub.end()); res.insert(res.end(), kyber_ciphertext.begin(), kyber_ciphertext.end()); + res.insert(res.end(), ecc_ciphertext.begin(), ecc_ciphertext.end()); res.insert(res.end(), kyber_pub.begin(), kyber_pub.end()); + res.insert(res.end(), ecc_pub.begin(), ecc_pub.end()); res.push_back(static_cast(alg_id)); return res; } @@ -95,8 +95,8 @@ KMAC256::Key_K(const std::vector &ecc_key_share, RNP_LOG_U8VEC(" - mlkemKeyShare: %s", kyber_key_share); #endif - res.insert(res.end(), ecc_key_share.begin(), ecc_key_share.end()); res.insert(res.end(), kyber_key_share.begin(), kyber_key_share.end()); + res.insert(res.end(), ecc_key_share.begin(), ecc_key_share.end()); return res; } From 6b91e82a6a54d761d54a15d1cfb56917d8f1816c Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Wed, 23 Oct 2024 16:22:59 +0200 Subject: [PATCH 09/15] update PQC test vectors; partially implement V6 OPS --- include/repgp/repgp_def.h | 6 + src/lib/crypto/signatures.cpp | 1 - src/lib/key_material.cpp | 8 +- src/lib/key_material.hpp | 4 +- src/lib/pgp-key.cpp | 6 +- src/librepgp/stream-dump.cpp | 27 +- src/librepgp/stream-packet.cpp | 122 +++++++- src/librepgp/stream-packet.h | 10 +- src/librepgp/stream-sig.cpp | 69 ++++- src/librepgp/stream-sig.h | 6 +- src/librepgp/stream-write.cpp | 16 + .../v4-eddsa-mlkem.pub.asc | 41 --- .../v4-eddsa-mlkem.sec.asc | 94 ------ .../draft-ietf-openpgp-pqc/v4-seipdv1.asc | 30 -- .../draft-ietf-openpgp-pqc/v4-seipdv2.asc | 30 -- .../v6-eddsa-mlkem.pub.asc | 41 --- .../v6-eddsa-mlkem.sec.asc | 93 ------ .../v6-eddsa-sample-message.asc | 34 ++ .../v6-eddsa-sample-pk.asc | 41 +++ .../v6-eddsa-sample-sk.asc | 44 +++ .../v6-mldsa-sample-message.asc | 103 ++++++ .../v6-mldsa-sample-pk.asc | 289 +++++++++++++++++ .../v6-mldsa-sample-sk.asc | 292 ++++++++++++++++++ .../draft-ietf-openpgp-pqc/v6-seipdv2.asc | 30 -- src/tests/ffi-enc.cpp | 20 +- src/tests/ffi-key.cpp | 40 ++- 26 files changed, 1085 insertions(+), 412 deletions(-) delete mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v4-eddsa-mlkem.pub.asc delete mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v4-eddsa-mlkem.sec.asc delete mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v4-seipdv1.asc delete mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v4-seipdv2.asc delete mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-mlkem.pub.asc delete mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-mlkem.sec.asc create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message.asc create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-pk.asc create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-message.asc create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-pk.asc create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc delete mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-seipdv2.asc diff --git a/include/repgp/repgp_def.h b/include/repgp/repgp_def.h index 91414872f7..3458034342 100644 --- a/include/repgp/repgp_def.h +++ b/include/repgp/repgp_def.h @@ -511,6 +511,12 @@ typedef enum { #endif } pgp_pkesk_version_t; typedef enum { PGP_SE_IP_DATA_V1 = 1, PGP_SE_IP_DATA_V2 = 2 } pgp_seipd_version_t; +typedef enum { + PGP_OPS_V3 = 3, +#if defined(ENABLE_CRYPTO_REFRESH) + PGP_OPS_V6 = 6 +#endif +} pgp_ops_version_t; /** Version. * OpenPGP has two different protocol versions: version 3 and version 4. diff --git a/src/lib/crypto/signatures.cpp b/src/lib/crypto/signatures.cpp index a932d3c7fe..247871c73b 100644 --- a/src/lib/crypto/signatures.cpp +++ b/src/lib/crypto/signatures.cpp @@ -179,7 +179,6 @@ signature_validate(const pgp_signature_t & sig, /* Finalize hash */ auto hval = signature_hash_finish(sig, hash, hdr); - /* compare lbits */ if (memcmp(hval.data(), sig.lbits.data(), 2)) { RNP_LOG("wrong lbits"); diff --git a/src/lib/key_material.cpp b/src/lib/key_material.cpp index 037c52ccb5..e68396dfec 100644 --- a/src/lib/key_material.cpp +++ b/src/lib/key_material.cpp @@ -1730,9 +1730,9 @@ Ed448KeyMaterial::write_secret(pgp_packet_body_t &pkt) const } bool -Ed448KeyMaterial::generate(const rnp_keygen_crypto_params_t ¶ms) +Ed448KeyMaterial::generate(rnp::SecurityContext &ctx, const KeyParams ¶ms) { - if (generate_ed448_native(¶ms.ctx->rng, key_.priv, key_.pub)) { + if (generate_ed448_native(&ctx.rng, key_.priv, key_.pub)) { RNP_LOG("failed to generate ED448 key"); return false; } @@ -1856,9 +1856,9 @@ X448KeyMaterial::write_secret(pgp_packet_body_t &pkt) const } bool -X448KeyMaterial::generate(const rnp_keygen_crypto_params_t ¶ms) +X448KeyMaterial::generate(rnp::SecurityContext &ctx, const KeyParams ¶ms) { - if (generate_x448_native(¶ms.ctx->rng, key_.priv, key_.pub)) { + if (generate_x448_native(&ctx.rng, key_.priv, key_.pub)) { RNP_LOG("failed to generate X448 key"); return false; } diff --git a/src/lib/key_material.hpp b/src/lib/key_material.hpp index fe4a653cf2..4f61149b27 100644 --- a/src/lib/key_material.hpp +++ b/src/lib/key_material.hpp @@ -550,7 +550,7 @@ class Ed448KeyMaterial : public KeyMaterial { bool parse_secret(pgp_packet_body_t &pkt) noexcept override; void write(pgp_packet_body_t &pkt) const override; void write_secret(pgp_packet_body_t &pkt) const override; - bool generate(const rnp_keygen_crypto_params_t ¶ms) override; + bool generate(rnp::SecurityContext &ctx, const KeyParams ¶ms) override; rnp_result_t verify(const rnp::SecurityContext & ctx, const pgp_signature_material_t & sig, const rnp::secure_vector &hash) const override; @@ -581,7 +581,7 @@ class X448KeyMaterial : public KeyMaterial { bool parse_secret(pgp_packet_body_t &pkt) noexcept override; void write(pgp_packet_body_t &pkt) const override; void write_secret(pgp_packet_body_t &pkt) const override; - bool generate(const rnp_keygen_crypto_params_t ¶ms) override; + bool generate(rnp::SecurityContext &ctx, const KeyParams ¶ms) override; rnp_result_t encrypt(rnp::SecurityContext & ctx, pgp_encrypted_material_t &out, const uint8_t * data, diff --git a/src/lib/pgp-key.cpp b/src/lib/pgp-key.cpp index a01e498b8f..0103e13cfa 100644 --- a/src/lib/pgp-key.cpp +++ b/src/lib/pgp-key.cpp @@ -2406,8 +2406,10 @@ pgp_key_t::sign_init(rnp::RNG & rng, } #if defined(ENABLE_CRYPTO_REFRESH) if (version == PGP_V6) { - sig.salt.resize(rnp::Hash::size(sig.halg) / 2); - rng.get(sig.salt.data(), sig.salt.size()); + size_t tmp_salt_size; + assert(pgp_signature_t::v6_salt_size(sig.halg, &tmp_salt_size)); + sig.salt_size = tmp_salt_size; + rng.get(sig.salt, sig.salt_size); } #endif } diff --git a/src/librepgp/stream-dump.cpp b/src/librepgp/stream-dump.cpp index 2a9951acdf..9c27a3f0ef 100644 --- a/src/librepgp/stream-dump.cpp +++ b/src/librepgp/stream-dump.cpp @@ -1402,7 +1402,19 @@ stream_dump_one_pass(pgp_source_t *src, pgp_dest_t *dst) dst_print_sig_type(dst, NULL, onepass.type); dst_print_halg(dst, NULL, onepass.halg); dst_print_palg(dst, NULL, onepass.palg); - dst_print_keyid(dst, "signing key id", onepass.keyid); +#if defined(ENABLE_CRYPTO_REFRESH) + if (onepass.version == PGP_OPS_V6) { + dst_print_vec(dst, "salt", onepass.salt, false); + } +#endif + if (onepass.version == PGP_OPS_V3) { + dst_print_keyid(dst, "signing key id", onepass.keyid); + } +#if defined(ENABLE_CRYPTO_REFRESH) + if (onepass.version == PGP_OPS_V6) { + dst_print_fp(dst, NULL, onepass.fp); + } +#endif dst_printf(dst, "nested: %d\n", (int) onepass.nested); indent_dest_decrease(dst); @@ -2550,9 +2562,20 @@ stream_dump_one_pass_json(pgp_source_t *src, json_object *pkt) if (!obj_add_intstr_json(pkt, "public key algorithm", onepass.palg, pubkey_alg_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } - if (!json_add(pkt, "signer", onepass.keyid)) { +#if defined(ENABLE_CRYPTO_REFRESH) + if (onepass.version == PGP_OPS_V6 && + !json_add(pkt, "salt", (const char *) onepass.salt.data(), onepass.salt.size())) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } +#endif + if (onepass.version == PGP_OPS_V3 && !json_add(pkt, "signer", onepass.keyid)) { + return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE + } +#if defined(ENABLE_CRYPTO_REFRESH) + if (onepass.version == PGP_OPS_V6 && !json_add(pkt, "signer", onepass.fp)) { + return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE + } +#endif if (!json_add(pkt, "nested", (bool) onepass.nested)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } diff --git a/src/librepgp/stream-packet.cpp b/src/librepgp/stream-packet.cpp index aa12e4447f..68c8cf8ca4 100644 --- a/src/librepgp/stream-packet.cpp +++ b/src/librepgp/stream-packet.cpp @@ -1149,7 +1149,7 @@ pgp_pk_sesskey_t::parse(pgp_source_t &src) return RNP_ERROR_BAD_FORMAT; } fp.length = fp_len; - if (fp.length && (fp.length != fp_and_key_ver_len - 1)) { + if (fp.length && (fp.length + 1 != fp_and_key_ver_len)) { RNP_LOG("size mismatch (fingerprint size and fp+key version length field)"); return RNP_ERROR_BAD_FORMAT; } @@ -1260,7 +1260,7 @@ pgp_pk_sesskey_t::parse_material(pgp_encrypted_material_t &material) if ((version == PGP_PKSK_V3) && !do_encrypt_pkesk_v3_alg_id(alg)) { if (!pkt.get(bt)) { RNP_LOG("failed to get salg"); - return RNP_ERROR_BAD_FORMAT; + return false; } enc_sesskey_len -= 1; salg = (pgp_symm_alg_t) bt; @@ -1289,7 +1289,7 @@ pgp_pk_sesskey_t::parse_material(pgp_encrypted_material_t &material) if ((version == PGP_PKSK_V3) && !do_encrypt_pkesk_v3_alg_id(alg)) { if (!pkt.get(bt)) { RNP_LOG("failed to get salg"); - return RNP_ERROR_BAD_FORMAT; + return false; } enc_sesskey_len -= 1; salg = (pgp_symm_alg_t) bt; @@ -1331,7 +1331,7 @@ pgp_pk_sesskey_t::parse_material(pgp_encrypted_material_t &material) if ((version == PGP_PKSK_V3) && !do_encrypt_pkesk_v3_alg_id(alg)) { if (!pkt.get(bt)) { RNP_LOG("failed to get salg"); - return RNP_ERROR_BAD_FORMAT; + return false; } salg = (pgp_symm_alg_t) bt; wrapped_key_len--; @@ -1441,7 +1441,20 @@ pgp_one_pass_sig_t::write(pgp_dest_t &dst) const pktbody.add_byte(type); pktbody.add_byte(halg); pktbody.add_byte(palg); - pktbody.add(keyid); +#if defined(ENABLE_CRYPTO_REFRESH) + if (version == PGP_OPS_V6) { + pktbody.add_byte(salt.size()); + pktbody.add(salt); + } +#endif + if (version == PGP_OPS_V3) { + pktbody.add(keyid); + } +#if defined(ENABLE_CRYPTO_REFRESH) + if (version == PGP_OPS_V6) { + pktbody.add(fp.fingerprint, fp.length); + } +#endif pktbody.add_byte(nested); pktbody.write(dst); } @@ -1456,27 +1469,106 @@ pgp_one_pass_sig_t::parse(pgp_source_t &src) return res; } - uint8_t buf[13] = {0}; - if ((pkt.size() != 13) || !pkt.get(buf, 13)) { + /* version */ + if ((pkt.size() < 1) || !pkt.get(version)) { return RNP_ERROR_BAD_FORMAT; } - /* version */ - if (buf[0] != 3) { + switch ((pgp_ops_version_t) version) { + case PGP_OPS_V3: + return parse_v3(pkt); +#if defined(ENABLE_CRYPTO_REFRESH) + case PGP_OPS_V6: + return parse_v6(pkt); +#endif + default: RNP_LOG("wrong packet version"); return RNP_ERROR_BAD_FORMAT; } - version = buf[0]; +} + +#if defined(ENABLE_CRYPTO_REFRESH) +rnp_result_t +pgp_one_pass_sig_t::parse_v6(pgp_packet_body_t &pkt) +{ + uint8_t buf[32]; + uint8_t salt_size; + + /* packet can't be smaller fo v6 */ + const size_t min_size = 54; + if ((pkt.size() < min_size)) { + return RNP_ERROR_BAD_FORMAT; + } + /* version */ + version = PGP_OPS_V6; + if (!pkt.get(buf, 4)) { + return RNP_ERROR_BAD_FORMAT; + } + /* signature type */ + type = (pgp_sig_type_t) buf[0]; + /* hash algorithm */ + halg = (pgp_hash_alg_t) buf[1]; + /* pk algorithm */ + palg = (pgp_pubkey_alg_t) buf[2]; + + /* salt */ + salt_size = buf[3]; + size_t expect_salt_size; + if (!pgp_signature_t::v6_salt_size(halg, &expect_salt_size)) { + RNP_LOG("invalid halg"); + return RNP_ERROR_BAD_FORMAT; + } + if (salt_size != expect_salt_size) { + RNP_LOG("invalid salt size"); + return RNP_ERROR_BAD_FORMAT; + } + salt.resize(salt_size); + if (!pkt.get(salt.data(), salt.size())) { + RNP_LOG("failed to get salt"); + return RNP_ERROR_BAD_FORMAT; + } + + /* fingerprint */ + std::vector fp_vec; + fp_vec.resize(PGP_FINGERPRINT_V6_SIZE); + if (!pkt.get(fp_vec.data(), fp_vec.size())) { + RNP_LOG("failed to get fingerprint"); + return RNP_ERROR_BAD_FORMAT; + } + fp = pgp_fingerprint_t(fp_vec); + + /* nested flag */ + if (!pkt.get(buf, 1)) { + RNP_LOG("failed to get nested flag"); + return RNP_ERROR_BAD_FORMAT; + } + nested = buf[0]; + + if (pkt.left()) { + RNP_LOG("trailing bytes in V6 OPS packet."); + return RNP_ERROR_BAD_FORMAT; + } + return RNP_SUCCESS; +} +#endif + +rnp_result_t +pgp_one_pass_sig_t::parse_v3(pgp_packet_body_t &pkt) +{ + uint8_t buf[12] = {0}; + if ((pkt.size() != 13) || !pkt.get(buf, 12)) { + return RNP_ERROR_BAD_FORMAT; + } /* signature type */ - type = (pgp_sig_type_t) buf[1]; + type = (pgp_sig_type_t) buf[0]; /* hash algorithm */ - halg = (pgp_hash_alg_t) buf[2]; + halg = (pgp_hash_alg_t) buf[1]; /* pk algorithm */ - palg = (pgp_pubkey_alg_t) buf[3]; + palg = (pgp_pubkey_alg_t) buf[2]; /* key id */ static_assert(std::tuple_size::value == PGP_KEY_ID_SIZE, "pgp_one_pass_sig_t.keyid size mismatch"); - memcpy(keyid.data(), &buf[4], PGP_KEY_ID_SIZE); + memcpy(keyid.data(), &buf[3], PGP_KEY_ID_SIZE); /* nested flag */ - nested = buf[12]; + nested = buf[11]; return RNP_SUCCESS; } diff --git a/src/librepgp/stream-packet.h b/src/librepgp/stream-packet.h index 25445d27cb..7a1f431e8a 100644 --- a/src/librepgp/stream-packet.h +++ b/src/librepgp/stream-packet.h @@ -220,10 +220,18 @@ typedef struct pgp_one_pass_sig_t { pgp_hash_alg_t halg{}; pgp_pubkey_alg_t palg{}; pgp_key_id_t keyid{}; - unsigned nested{}; +#if defined(ENABLE_CRYPTO_REFRESH) + pgp_fingerprint_t fp{}; + std::vector salt{}; +#endif + unsigned nested{}; void write(pgp_dest_t &dst) const; rnp_result_t parse(pgp_source_t &src); + + private: + rnp_result_t parse_v3(pgp_packet_body_t &pkt); + rnp_result_t parse_v6(pgp_packet_body_t &pkt); } pgp_one_pass_sig_t; /** Struct to hold userid or userattr packet. We don't parse userattr now, just storing the diff --git a/src/librepgp/stream-sig.cpp b/src/librepgp/stream-sig.cpp index 22daab750f..3702f96266 100644 --- a/src/librepgp/stream-sig.cpp +++ b/src/librepgp/stream-sig.cpp @@ -734,8 +734,35 @@ pgp_signature_t::matches_onepass(const pgp_one_pass_sig_t &onepass) const if (!has_keyid()) { return false; } - return (halg == onepass.halg) && (palg == onepass.palg) && (type_ == onepass.type) && - (onepass.keyid == keyid()); + /* check sig and OPS packet version binding (V3) */ + if (onepass.version == PGP_OPS_V3) { + if (version != PGP_V3 && version != PGP_V4 && version != PGP_V5) { + return false; + } + } +#if defined(ENABLE_CRYPTO_REFRESH) + /* checks for V6 */ + if (onepass.version == PGP_OPS_V6) { + /* check version binding) */ + if (onepass.version == PGP_OPS_V6 && version != PGP_V6) { + return false; + } + /* check fp */ + if (onepass.fp != keyfp()) { + return false; + } + /* check salt (V6) */ + if (onepass.salt != salt) { + return false; + } + } +#endif + /* check keyid (V3) */ + if (onepass.version == PGP_OPS_V3 && (onepass.keyid != keyid())) { + return false; + } + /* check the remaining common attributes */ + return (halg == onepass.halg) && (palg == onepass.palg) && (type_ == onepass.type); } bool @@ -1003,7 +1030,12 @@ pgp_signature_t::parse(pgp_packet_body_t &pkt) RNP_LOG("not enough data for v6 salt size octet"); return RNP_ERROR_BAD_FORMAT; } - if (salt_size != rnp::Hash::size(halg) / 2) { + size_t expect_salt_size; + if (!pgp_signature_t::v6_salt_size(halg, &expect_salt_size)) { + RNP_LOG("invalid halg"); + return RNP_ERROR_BAD_FORMAT; + } + if (salt_size != expect_salt_size) { RNP_LOG("invalid salt size"); return RNP_ERROR_BAD_FORMAT; } @@ -1262,3 +1294,34 @@ pgp_signature_t::fill_hashed_data() } hashed_data.assign(hbody.data(), hbody.data() + hbody.size()); } + +#if defined(ENABLE_CRYPTO_REFRESH) +bool +pgp_signature_t::v6_salt_size(pgp_hash_alg_t halg, size_t *salt_size) +{ + switch (halg) { + case PGP_HASH_SHA256: + *salt_size = 16; + break; + case PGP_HASH_SHA224: + *salt_size = 16; + break; + case PGP_HASH_SHA384: + *salt_size = 24; + break; + case PGP_HASH_SHA512: + *salt_size = 32; + break; + case PGP_HASH_SHA3_256: + *salt_size = 16; + break; + case PGP_HASH_SHA3_512: + *salt_size = 32; + break; + default: + RNP_LOG("no V6 salt size for algorithm"); + return false; + } + return true; +} +#endif diff --git a/src/librepgp/stream-sig.h b/src/librepgp/stream-sig.h index 1cecaa779e..48fbc541d4 100644 --- a/src/librepgp/stream-sig.h +++ b/src/librepgp/stream-sig.h @@ -66,7 +66,11 @@ typedef struct pgp_signature_t { #if defined(ENABLE_CRYPTO_REFRESH) /* v6 - only fields */ - std::vector salt; + uint8_t salt[PGP_MAX_SALT_SIZE_V6_SIG]; + uint8_t salt_size; + + /* maps halg to V6 salt size. Returns false if no mapping exists. */ + static bool v6_salt_size(pgp_hash_alg_t halg, size_t *salt_size); #endif pgp_signature_t() diff --git a/src/librepgp/stream-write.cpp b/src/librepgp/stream-write.cpp index f50cd3601a..a0939b2dc6 100644 --- a/src/librepgp/stream-write.cpp +++ b/src/librepgp/stream-write.cpp @@ -1450,6 +1450,22 @@ signed_add_signer(pgp_dest_signed_param_t *param, rnp_signer_info_t *signer, boo } } +#if defined(ENABLE_CRYPTO_REFRESH) + // Do not create OPS for V6 (currently not implemented) + if (sinfo.key->version() == PGP_V6) { + sinfo.onepass.version = 0; + try { + param->siginfos.push_back(sinfo); + return RNP_SUCCESS; + } catch (const std::exception &e) { + /* LCOV_EXCL_START */ + RNP_LOG("%s", e.what()); + return RNP_ERROR_OUT_OF_MEMORY; + /* LCOV_EXCL_END */ + } + } +#endif + // Setup and add onepass sinfo.onepass.version = 3; sinfo.onepass.type = PGP_SIG_BINARY; diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v4-eddsa-mlkem.pub.asc b/src/tests/data/draft-ietf-openpgp-pqc/v4-eddsa-mlkem.pub.asc deleted file mode 100644 index 5b1c3551fb..0000000000 --- a/src/tests/data/draft-ietf-openpgp-pqc/v4-eddsa-mlkem.pub.asc +++ /dev/null @@ -1,41 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -=dPFW ------END PGP PUBLIC KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v4-eddsa-mlkem.sec.asc b/src/tests/data/draft-ietf-openpgp-pqc/v4-eddsa-mlkem.sec.asc deleted file mode 100644 index ba7a502109..0000000000 --- a/src/tests/data/draft-ietf-openpgp-pqc/v4-eddsa-mlkem.sec.asc +++ /dev/null @@ -1,94 +0,0 @@ ------BEGIN PGP PRIVATE KEY BLOCK----- - -xVgEUdDGgBYJKwYBBAHaRw8BAQdAhoSK5cJt9N37EE1UjPqp8EXhAvOBCYikgtcg -HMUso9MAAPwIdkHSrZmM4/Res+3qv1UT7kV5OAr6VO0M2P0ZPdAFiBICzS5QUUMg -dXNlciAoVGVzdCBLZXkpIDxwcWMtdGVzdC1rZXlAZXhhbXBsZS5jb20+wo8EExYK -AEEFAlHQxoAJEMYq3A3dHt1zFiEEsum1MtVb1ih+x54XxircDd0e3XMCGwMCHgkC -GQEDCwkHAxUKCAIWAAUnCQIHAgAAooUA/jV775USotWqnMYHmrqaCWsUduO0cLxS -4U7CuItZnfMJAPwLAyXS8awEJ92Ll52fQ2ESsAkJ4f/cjdHoP9V+BZbSBsddBFHQ -xoASCisGAQQBl1UBBQEBB0Dfrrz6gEv3iM2ULhupwUD4qABPIAwaNyVYDT2euXaS -dgMBCgkAAP9Q+XMh/cX9bvDH6mbpoGjZkeYkw1NO6y5NQEDmvDnEIBN+wngEGBYK -ACoFAlHQxoAJEMYq3A3dHt1zFiEEsum1MtVb1ih+x54XxircDd0e3XMCGwwAAI/D -AP9yG1KzQlWnMNMjyvpkxWhAjyIVxbtr+4WsXUdTqMMQkgD/SeI376LSUoB6s/oL -P10oFOJ86NjwfawQvIqa0CPIkgfHzYkEUdDGgGnWzS/qVrM3Wy7ifldXrJMRIq+r -iGRtWY4Hr1s0GXm+fmMDoLIGUnUCOM0BzzdQgEAcnlVFCZQ4NmlwbChkHI5nFiIl -cGQhrqzzxOzhPJrniyRZJMb3gBMXQO6yCx66G7fHAJ73J1AcFTNWyszaIcXnazHX -OBSpnSMrvQfZIfV3tyW2Xhg6KjhDD6/TsrBiigPGGlZwcPtAh/EbkwR1xYlnU0mX -tlwrlHgWvkwlcXOgdz4VUiDGPIJRGIh6LXe1dobCUjYVZPEKmf9TN2o8oSiVRr8L -GZF1jXyqLlHloMSbJiV1m6iZH8DjTWMBYRRAOVr1Ly7MDqrwJoN0CQFnx/Hqum+2 -czgxlsWLtGvADUwaPodwH9MHp4tXJ/HsOOO7z6bYdCNpAySqpSmzCaNzlXppw54n -bD+0UE70dxh0UHiGnoJQXy5mkcG2gTWpwC7bZ+nbCBgcJF/IHBbIWbYQVLDTeP+z -LKnDt/iAoJ5qgeF1wuC7pwsaQy7EUZgClZ0ivkdLyC6ZImikkaczV/VcrxeZZRqC -GqfxQ05QJOAiGFNhvBvDclXcaXYWibxQgyFlGUM1rbR8XZJzVjbihw0pfiVnustU -xYhsroqybX6iJVdAxVNiZwrMZ4VqifErJ1lYbYImF+jKQ6/zYZrrODDmLy0xZqhq -mC5jsE1owzTDzEPnibtTEWiKbShTmJmxbhtQwhW7jsvKhQXbSvr7Nsh0vXzWEGim -IkpBEXycePOnVSens94Rpa2jqgjLJhgalqocm07pNXFMeyJAhYVnHUuCsQzgrkNc -ncbVe85GzsW6S/8MzdtKD9MGy3XHlKKByeF1oxcWEnBEQZ4JhpOmIHV7TVRhHa8L -tIQ4HmCADposq1OTiAbxfYP6RtiLyemxDJaFLdaDSRSXIf5ALgxaysUxe57Qh7uA -Qh5WejIJy6cDZtUYqtoLg8KDegxKSmo3hy2nsReMgc6SFU/ziHNWWQAtSjHrbFry -ruaAJAmVGKj2UoqACMQlDpZkQYF2po8byQx7TIGnXwmGisygomwjTGocO5LDqoyS -uORISmhcXbvcXtRWnQMafPAhpb6Sfm4JGic7W3/EcgmRcWiLnbnzNeBgirQgqTky -kBRMycBAzgglsq5CJOHWZOoJTvlBHXBiq3z2ddY4hzckCeqQYwCrn08qChsLHuX1 -r5ZxFE+XE6+YRvwIYEKrBTDzxNppnZTMFkGhgHWXuZcSnYQAxiSbVHTkjvcEC3k8 -HHGovlujZInkNlQGk2KQjCWCI2JgFvIBBcswMt8Jmr9Jpa4zvv08Zi60DJpWYonH -N+uSQ1FbxCm5tM6JJaKSjLYQxm6zfZ0Lxc0XP90SUKg4Ux+Al0y1jH7VgjWmGrP1 -geoHgvP8RWlHbW+rhBWsYmAATawUdPZAg/rcODM0fzRpe5CWdnjIhRqUAjQruB4A -n2iXWu5DymzgV6ajOB/3VKxYvup0mRULOwZsqHHIzJGCxlesMIecccRUT2IWaxFJ -h4m1igg8zS9hG5/yQr3bJH2UbxX2o453u58wqvJBYOvhWDsITKAQMyhSD9iGA8Pq -AAs1utxWaATaNH1qvDxDrdiHCYadNeTVxYYb8HVRBLaWNlG1lYjvl+WGf5t9AMC5 -EKxgiozRC4yyd1oYV1+fv8g5eMz2pBWB5tuvE5ootGtwzIWSkRmGUfEzZpLCIWAF -/9CWtmnPPiygQZecuzUDsTQRHnIANfWVhGZFmFh8qxc81IZKTPtgBMgW8ewE3oiJ -cac8BHo5cEiTxeVDXXqEMCOn4jQCtKU8+ogQLhF6OvVpV2A9eKlvVberhBqu2+lC -KDt2YpRjb2Bm5lqWDLUAiWa8rMTMwQmfybFp7Zi25pDPHpEwWvGGR6sjVYMfVRR8 -HlOV6csYJTejEPghZih0dwEHdhUSe9Su+HNjsiunyNg042mGkOE/n1SGZ2cVkOMB -iwyDm4uo9bG8X9akvOdT4EA3Pfg5DLAOIUILJlsrBPBRvIG5bulUdOtMfkMMUHOF -O3O8FvyjpUc43tOgmvnEcCuuraqIjzokM6pHYjYjySmipMxFi8anwZIix/sUBclg -UtIMo0KBY+aTwDGoOJkERWp8zgdcplfLYYEzlCWhm1JAbabKQpummohwpUErZ9gy -1NtuMJhDxxtb8MMylwWHklpwhkFcLgW/rIkLEte15zuSiGcrOJYpUEpnP/edSdyn -YOutidNbg5tLxaZTiKYcUFcdZ1jI7ows9Ri4v4xJ6MxGJOSIPGWieDw1b9GTehS/ -uCp++UJzCMQUYfHI/nYbDAyWPbx9piVkCIychNhrGurIqPMEUBCwXNF60hhGXlep -TLoldts3W0xX3ROmD8gPqEUa8pujI1oeULiL3vlfb1deXSu6evLMPoyWyRKEFCY0 -+Fe1G2RX1CyjYKkW5heqWkJixHS7tItCksAgFTTCWcUammBEA8NUuEeg3jO8nVA4 -aKFrfOoSrYbHqsmO1AJfCDh21iMUOVAefLTFIsavLuFO8OmOh7cXcOQWG6G5ZdUt -SQQxJJK6mKka5TZlp2GXyGzFVgdD1ddkMHMu9gB96SJjsodDQseklAldfft66xt6 -UIbEVbwVCDctzPZ1o/pwKeu2GJa+D6RSEPNRrZeHF7OVq8pnnCQE/HUsrLcqPZhn -/8K5MvhphdmoUQl5fmQZoIRqRxFoDqFvJ/qETBsTwwkkgLwBzSEe4+ubbchq3Jp5 -1LVmmZG5BxVe9UWzPirPqKXPu4oArjEqtRJ5MARI1NifzMvKJpGuqhMIh8UQraGf -KNJBxwN99Aq7GCYmkyYvo9wNUMJrYfa3TXh0GwBhqwxObtzAQZzGXGl9kVQEI9Q1 -upTFQKgddScJIsoIdzSGfJlkVtSj6lsqmDdkHMPCa9yhk2Ikn9E/kbQ371ca2iZ2 -4FAIIXHNeULH4qIc8ScjO1epIuerd9MLJdi5dwR9zAwNe8GznNGMi5HE2BJyS5gZ -8ht3/Xm88lIXRil6bnTOBYQ9RDoIHNU2EFamnBO9jUu92XBGqgRv9iKAVTmPr7i5 -qMe8vEU0PeOjt2d4aHlZ/cO+NRO1YvHMQfxSGpjPQMRvUGoUAOOkndYVlGw3VzKw -7pFerytKJaozmpuGFCVLhlJYn9C6oeCfPQQjy1ydULIBe6DKUycbvAIjK3Qj4jum -I2dp8RV3JHRmcxWzngJuj7nFyfeKBecwZesMqXl3YwOgsgZSdQI4zQHPN1CAQBye -VUUJlDg2aXBsKGQcjmcWIiVwZCGurPPE7OE8mueLJFkkxveAExdA7rILHrobt8cA -nvcnUBwVM1bKzNohxedrMdc4FKmdIyu9B9kh9Xe3JbZeGDoqOEMPr9OysGKKA8Ya -VnBw+0CH8RuTBHXFiWdTSZe2XCuUeBa+TCVxc6B3PhVSIMY8glEYiHotd7V2hsJS -NhVk8QqZ/1M3ajyhKJVGvwsZkXWNfKouUeWgxJsmJXWbqJkfwONNYwFhFEA5WvUv -LswOqvAmg3QJAWfH8eq6b7ZzODGWxYu0a8ANTBo+h3Af0weni1cn8ew447vPpth0 -I2kDJKqlKbMJo3OVemnDnidsP7RQTvR3GHRQeIaeglBfLmaRwbaBNanALttn6dsI -GBwkX8gcFshZthBUsNN4/7MsqcO3+ICgnmqB4XXC4LunCxpDLsRRmAKVnSK+R0vI -LpkiaKSRpzNX9VyvF5llGoIap/FDTlAk4CIYU2G8G8NyVdxpdhaJvFCDIWUZQzWt -tHxdknNWNuKHDSl+JWe6y1TFiGyuirJtfqIlV0DFU2JnCsxnhWqJ8SsnWVhtgiYX -6MpDr/Nhmus4MOYvLTFmqGqYLmOwTWjDNMPMQ+eJu1MRaIptKFOYmbFuG1DCFbuO -y8qFBdtK+vs2yHS9fNYQaKYiSkERfJx486dVJ6ez3hGlraOqCMsmGBqWqhybTuk1 -cUx7IkCFhWcdS4KxDOCuQ1ydxtV7zkbOxbpL/wzN20oP0wbLdceUooHJ4XWjFxYS -cERBngmGk6YgdXtNVGEdrwu0hDgeYIAOmiyrU5OIBvF9g/pG2IvJ6bEMloUt1oNJ -FJch/kAuDFrKxTF7ntCHu4BCHlZ6MgnLpwNm1Riq2guDwoN6DEpKajeHLaexF4yB -zpIVT/OIc1ZZAC1KMetsWvKu5oAkCZUYqPZSioAIxCUOlmRBgXamjxvJDHtMgadf -CYaKzKCibCNMahw7ksOqjJK45EhKaFxdu9xe1FadAxp88CGlvpJ+bgkaJztbf8Ry -CZFxaIudufM14GCKtCCpOTKQFEzJwEDOCCWyrkIk4dZk6glO+UEdcGKrfPZ11jiH -NyQJ6pBjAKufTyoKGwse5fWvlnEUT5cTr5hG/AhgQqsFMPPE2mmdlMwWQaGAdZe5 -lxKdhADGJJtUdOSO9wQLeTwccai+W6NkieQ2VAaTYpCMJYIjYmAW8gEFyzAy3wma -v0mlrjO+/TxmLrQMmlZiicc365JDUVvEKbm0zoklopKMthDGbrN9nQvFzRc/3RJQ -qDhTH4CXTLWMftWCNaYas/WB6geC8/xFaUdtb6uEFaxiYABNrBR09kCD+tw4MzR/ -NGl7kJZ2eMiFGpQCNCu4HgCfaJda7kPKbOBXpqM4H/dUrFi+6nSZFQs7BmyoccjM -kYLGV6wwh5xxxFRPYhZrEUmHibWKCDzNL2Ebn/JCvdskfZRvFfajjne7nzCq8kFg -6+FYOwhMoBAzKFIP2IYDw+oACzW63FZoBNo0fWq8PEOt2IcJhp015NXFhhvwdVEE -tpY2UbWViO+X5YZ/m30EFhqD2sbN4HJ/Sv2SB7DadONGI5Sj0tnqRWZ//nA4CLZo -y1LriIK38pV3lBCLv2M9vynHoyXTFco3BqTUGUEjbDnCeAQYFgoAKgUCUdDGgAkQ -xircDd0e3XMWIQSy6bUy1VvWKH7HnhfGKtwN3R7dcwIbDAAA8PEA/16fgmhfrX12 -GXFXcTGO8MKQTihxz2djD4aki7fVX+ZAAP9UT/A3jAfqvFNp+ecYkkZ8T+vnXR4P -0O22blDNAr/tDA== -=q5En ------END PGP PRIVATE KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v4-seipdv1.asc b/src/tests/data/draft-ietf-openpgp-pqc/v4-seipdv1.asc deleted file mode 100644 index 434b61328f..0000000000 --- a/src/tests/data/draft-ietf-openpgp-pqc/v4-seipdv1.asc +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN PGP MESSAGE----- - -wcPUA+RAz7r/1vNXafOEf/qo/UCjgP5MHXsUdzusv+Xtwa/q5/gtvWSKENMEm/ef -mwvHzlOW3jyHPr3wNkiRGNmZdEgHlRUxOF67AKfUqX1/H+RawlXrnu2O/7xDfn09 -1DOeYQYsG+VQe5eDfPOh6EAqvhglNG29enkwukjQuWJ0U2+SywRlh0J49oG0lZZi -tEonE6BX+dKo7JFAAW27I4+I15zamWCm7C2qXtcjlMq+68U6isstfJYBgCFoJLRV -9ME/JlwVQ/3OA4eagh1Ysw/s0Kbl8dWc/U+pUVINfLFnZbr8UnRx5QVjo47HzV9y -IPQcl1ETnrQqg4jH5cUB0wVB/OGSHEH1l1q7OwO4TBx9seRKza3wzHgXQyQn4jJn -WxG4Uf0rWwa/dbXpoGdxye7Di0HhFJBp4aPaPEm9RwmeD06HPGyurgQS2heW+ICR -X9q6HjxKmbIToWau7sEUZQh1isRD923zmZO1Cceiefwvv8RDErBaYRRfhoDHnwlf -haUkGDH55GC/mFgMUst7XjYLTpjTtM32bHHVoYyx3edk3V4C32dRP4geZRlq8MKb -3eNvTyZHPBXVeVjB2XVenMkju2qQvjAvr7xkJk5QvqRGZ3qy5JKPHjupPKMKR6Wv -90WtP9OPf6geE4K8g8dc4yqhqEf84IpWfwyUjglPwc8G7QURyc14INSgnVKQ8nzJ -26Y0YSuBG599cwnBXIv7TLOsrmpcR+3O/OiqztEXwBSFalWqC6SScWr51+K4RHpi -0Dg3GYOdeu9V12ofWhh5eoXiQxiThsDc0cYD2LwwITb/sXJW6jisvTSQSszuDJsg -w4LJGVIgk94GwBeYCa4w+YbFD/6bC+nd9hze5iWyAt4jx2XuYWHvI95wUvRal4Y6 -LcYKShZslef/9sZlrJ3O+oM8Tc+xjr7w860cw9bHBaRA3oZfDwVLRbJ63bh6dzLS -8Vj4UL0NiK9AlokOTlYTatKj2tkLaD6snZ5QaS916NniwvBiSozyCWKFPvKbIA5t -Fy7bWMk70wd7FJ5rOYsRvWOodcxxfYHLyOYCRikP5sOgelHJfIcqZZM/iMyKrUl5 -QXTe5SUMFKuAAWkv1nFgJqaBGkkYUN+aL6um4ogKJKxBArhUXtz5jp1zLQe6w7Pf -XtJ3rtnLMNIGbaJjN6jlSqB1lF88ljU6yL8lsfNfGuq6eLsNz1QD9sFoZyNKGVF8 -uGcX8KH07hyNCOPbn4jMZqnLfcEwT971KNeW0NNNLaVIjiYtYrEInYpnGP1fW0G7 -4ic4/CGZ0Jsti39rTPdz76n14EV5+HGGNtO92nXr7HhRvlvehZO+pPN2pPvAOQgp -378jEDnhdcFIUnWjaSN4HLjxN9X3tWosa9stVatcOB5n01+QAqdstgbARXiYhzZI -QOy5bk7GzM/xaD+4iMlsMgmhdvI3EuMrvzRHx95YvU+tc8UBhVtMpcxgp5tiPtw7 -wEFyPWYqTA6c+YGTZFzHNhqJ+DqLTlKP6+uYet4pCY6YLcu4z0JMG60Q4/MnuF2u -7pjPG7aRNGwr2/JFeaumSFbs5SrDPwDSOQGz9bQ6fbfGNm9Iw/Gq4EYrNx0raEVt -E1FD8+nRXcS1PribkWlu/qNd0yM+tXMXlryR6wAV3R4p9Q== -=h3Km ------END PGP MESSAGE----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v4-seipdv2.asc b/src/tests/data/draft-ietf-openpgp-pqc/v4-seipdv2.asc deleted file mode 100644 index 2c54fd06a0..0000000000 --- a/src/tests/data/draft-ietf-openpgp-pqc/v4-seipdv2.asc +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN PGP MESSAGE----- - -wcPhBhUEvWfZg4iBPoi/NJDz5EDPuv/W81dp/h3ny6MDFKIjw8Jva/pK0mlJJMbX -RtJ2+idWKUL4/Evq2JBL2pky3VeCkhJIAnKdMXBvuucpNC4xVhCREpPquOsQcJE1 -IyR5kdk0uOw/7T5i7i9zb3N8Mn4jObYNxem0PbD44xYfF4BcNZqWPhLSwdFp+uti -FDVcsAtaruQQnwcb0tE9KUxMHsH2QxuG6Xnm+a1v3fds/rp/DqrR1vTcwbeUBej9 -AcrWhRI+KrE80WtMqqB0nvj74Jsx7xrYKyDqi7C50PG1LwTfFmO1kuIOGwEVh3AL -f9vesNee8+QWqJHMuSaqTsndts7Pq2EQ1fzdsrhYvseFEuiITj6OHnvmERjhkVRt -M36v3cA8GmcKI2YLa+fNvyagjgkoUycjNopK94/KQk8DY4JnKEbMHJ1BvUQ6mXkZ -ZeQXEt9gBHMZvTPE4ZPKSR14tki9YbjCvGcklEx1BEhONMo+03C07H/AHMtk5Ia1 -BFDF3dRkOhyXLfJCNKhJL4Sq7Z07CqKYCCzEq6WgKQUlWmiJzEtZKmh61LX3FaVU -A8+iK56QBvYyFUv/T9mGIBvF6qM+1l9BUSDim81EEN3FVKuo5QJ+tEbuKOrDWMTQ -klx3iEmGFeW8/N0gxTMpDKQRqDbkhdwFoM1c1L61NeCfDvTiWWVQQg00Z/n38gGC -S4MC8szKWPPlmtPYsXbyITMLpErgbIfr3IPfwRkm1RWVPQ/T7dm0Iz8ufeVTAqv+ -4+WgeILpQbFf/PmKHlZJnyj+munVaLaV9ed8g/0ncfZ0Vn9sMzGXifAjJ7vT8t/2 -ojSqc1AQuhoNguI3xFUCaUEzmWzOl3ONZhgDr4WTJdvfW/8IHn2y0Kkn5buqLAO5 -LOvrksFgkuy3N21OzgI+H5SeCqOGt5NKgyMAUGKEa5a6ApG/DSecXJ38yr4hTG6k -IGxfd9dlezcdByrohFsRHC93XUSlTnxTqdYmuhK8x4me6QblJFEJhoDTr3lrKTQE -gXC5ncdTLKLLINnZgFRc0oFABKGATf/708nt/vkuhyNroCY2NxGKoem3M8P5radp -vKJ7qRi+ePBmxwPhb5mWVf7G5y509GgVhKUFUWCzvyoyp1HLtekARKrxBAElXo4D -RINA468O/Sx9rWDFFkaShsXqQEhe06C3IhoG6vqagdB82LYcisCWEAKd/hZE99Ub -EWawnQor/jrHhGNQId1nZEwpEE2/cndU3Np0mhoGF/kkjyGah8wxjFRZhX2b+w3M -jmyFyOFGog6SDK1dK6+Vdki3fJFvBCx/uLmuLiNQo2MYeG3B4W30jNDlkxZ2DA14 -1zPXL8S3t54ZCtqiqDO63SNW18o2SB9AJpmSYYF34LO29VJnWIK5/94tIsfwHbAg -P/JdfRmSXkUYDTwJcYR1SOp8IIWwhmybnYctHS/n1q+WnzMj0jJIe4SI9ifoJg6n -+ntRfsqR1B3fcTAKN0XK6pnNy+pJ1BCVDOYDz5RGsAa61wJdVK/mXEg7VyjMlf0t -EnPzSfwfotMPoZs9n+MuK/BO0AX8DqzY8SluBALG6Eu5OV+A0lQCCQIMeNuKCuzH -CoYERR8ds1jE9m+xLEGK7i1+zr5FSdzGowHh4xMo95Zk5JUubl5rYvcYbHgVVKKE -9mwM7/4Q5mXZ2xvsBftkujgamZM2UN9UYSA= ------END PGP MESSAGE----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-mlkem.pub.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-mlkem.pub.asc deleted file mode 100644 index 623953e31f..0000000000 --- a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-mlkem.pub.asc +++ /dev/null @@ -1,41 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -xioGUdDGgBsAAAAgsJV1qyvdl+EenEB4IFvP5/7Ci5XJ1rk8Yh967qV1rb3CrwYf -GwoAAABABQJR0MaAIqEGUjQyQjRSVAUCGc7/KG6cjkeeyIdX+VNUOImEoC19C1kC -GwMCHgkDCwkHAxUKCAIWAAUnCQIHAgAAAADhOyBW8CPDe5FreFmlonhfVhr2EPw3 -WFLyd6mKRhkQm3VBfw7Qw7eermL9Cr5O7Ah0JxmIkT18jgKQr9AwWa3nm2mcbjSo -ib2WVzm5EiW3f3lgflfrySQFpSICzPl2QcAcrgjNLlBRQyB1c2VyIChUZXN0IEtl -eSkgPHBxYy10ZXN0LWtleUBleGFtcGxlLmNvbT7CmwYTGwoAAAAsBQJR0MaAIqEG -UjQyQjRSVAUCGc7/KG6cjkeeyIdX+VNUOImEoC19C1kCGQEAAAAAg2ogTEbKVVlb -WsejQHkq7xo8ipM7dv6Hz2AekkJqupKVR+/oy+2j6ri+/B2K6k1v1y5quzirhs87 -fB5AxZC6ZoFDvC0kZOvo14fPF07wCx0jwJVOWuRFVsVw7pQJHbNzgkIAzsQKBlHQ -xoBpAAAEwLRbSSpvve2pIh3hHweqq2VdRo+7Zf7whYHyXM/UifsniwMKSrubvsmL -gCyiEwMip3ZlTSxIFDaFEMVtVvCSJ7XFZ0WslTJnZ/CENPgxbVgn6CC2b8UEb8ol -S3AxlSiqJSRP0OrOJdfPWJI1A+p7Vmw1CZQq2oVPUlE96SVUrFxfk7XCYpcTpIQb -+mFB4ULCesat5tud7TauUJpMKssUf0I74EUjahoR46pPReKzlSqfvhpgXSASZpBg -8IZBY7VbgTnLInGTTnErrScVlDnAwcdYvuZMQYO5EjS6LOxn1aVfU+iH+Rir2AyF -zsYl6ICHciPAsKKa+Sk7UPFBrIRG1qgn7FF0n5epHeiFCRNb87wSqlp0h+d8L3jP -mDq4zoQPKDViasoHYXLD7KoJTIxP2eGzjMRlg3oD9ph3ZnyOTIsx/4SDtxW3q+JU -8RFoI0dZEdURwaoIITWitldtPUmtBuJshceEDSWopuwLzBuVTnYDpTy94ZtDBKmg -PnmSmPOKZ6THucmiJGUmWmAKkyo7kWAwYRsE2ZYqLzIJFmZFzRLIThipiZhR/9h2 -GemQklMJqYs25cEGx6FWzXRv8Palm7yOAicH/ldHUOtU3oFIXthOatwSrQApJ7HH -vksx59ZtLFtBgHm5eRmYYleJsJLGCPssa7pK2hIwgLlmCLSAavFqYjuocWIYKLmw -5vNXXRWIjPBbTpVXbUO5U9F/67gggSWBJXCZlfgcluO422aN22m8aONiTgZtmjcC -2elci5yRKGBbeKmFTcVsZbpbY6ZCKFRyzbqmMGYe0mqN6lh7R5dNiBuJZQg04mYu -SzWCF3mumlJTRtlN9Miy6LyWApJSTQdgc3awS0mjUrgU1Ia0AjMFKcxJA6iHd6iA -xWMbUqxOSoTOTUlMr3ltpaNGEMGpaHwMoQs99xSI1zG9pYmfeIl6LfZSwnI4LsBv -NOBiUhNUC/aYIILEm7qjTpw5YdI+6jSl+palLlcMDzt0LgMN8rY6UlZJBGNFSAKS -NSWXdFYMByKKGSCj91TDWPlOLvWKntSLk5eLodhgmRGqx5GZECgWS4wDARY00rl1 -7dV53GejXrUtJaYcnam5pKoTSaPJTuY25Kyy+oB7aHpV0vA87JaeRCsqkjcS5IQK -dtceUskXNRa2f7CTrfQRhOGk0gSA4Jx8+Fw8uGWLGJx6m1lSyWcMX5HL7hJkFhEK -ebYjdALGXMV1wxNiUHCIvxCjX/AkwHEDvAN6qhULrcZlmngSbeBysOFud2a8PIS2 -p7RCAatO+TpFgoR+1CgVJIdiRpM0WrMfS9iBERhtYaLH1oUjBpcV7zpgNdkT4Clf -bTpgu3oPnWBogDjMXKUepSfFx0l1tNGRLCCFVit8xxA4Q+phutInyXUAHJiEfHIR -4jxTd/FwQ3pDoKxTesY+XsGtVJxe9oMrXSlt6uymn6zKQlQsw8odvHhp5/NWqkCh -9/xQvmIlERsVVjyJ0FNF/+HNT9KrECCj6+cujDbEN6UmRlFvlMcxFzYaTnWa1csh -SVCCa1aYZddWrDdxOwMfObUw8TukY7A2RqcdpmpA68SLoWwNAgtFG1xWV43yC/P3 -XTsqTmgHRUGboDkVs9K81+Byg4jhKWfCmwYYGwoAAAAsBQJR0MaAIqEGUjQyQjRS -VAUCGc7/KG6cjkeeyIdX+VNUOImEoC19C1kCGwwAAAAA5kEgPwatbx3FHPIy9J9m -GUEpUE03oRRPE8N4lJ2eAIMhciCEHp3BzYVGvW3OaPYmjcu4JTREPJM6HP7yR+ZE -g+Bld9lBSVmEdMJnOX2ZHOdEoRV4bm1U4aPuhrKL/d8lkIgM ------END PGP PUBLIC KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-mlkem.sec.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-mlkem.sec.asc deleted file mode 100644 index 92a249311b..0000000000 --- a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-mlkem.sec.asc +++ /dev/null @@ -1,93 +0,0 @@ ------BEGIN PGP PRIVATE KEY BLOCK----- - -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 -/d8lkIgM ------END PGP PRIVATE KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message.asc new file mode 100644 index 0000000000..075ba32528 --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message.asc @@ -0,0 +1,34 @@ +-----BEGIN PGP MESSAGE----- + +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 +-----END PGP MESSAGE----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-pk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-pk.asc new file mode 100644 index 0000000000..d1989d7547 --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-pk.asc @@ -0,0 +1,41 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc new file mode 100644 index 0000000000..cf84b5ab2a --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc @@ -0,0 +1,44 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +BoWQifC9dbHD5JNv0/6CMXFZagQABA== +-----END PGP PRIVATE KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-message.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-message.asc new file mode 100644 index 0000000000..ebeff16a59 --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-message.asc @@ -0,0 +1,103 @@ +-----BEGIN PGP MESSAGE----- + +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 +-----END PGP MESSAGE----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-pk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-pk.asc new file mode 100644 index 0000000000..ef527fd69b --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-pk.asc @@ -0,0 +1,289 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +R7PWnOYif6JqzzgsxWnC1nmVEAeWtw17s4tuke4cRh5owUNJBZZygKBJsn37H5cX +oZHw8uzmESNxgMxw9zVUoeOc+l4eX5ynM+FxmXhxIhOH1sQluFb1MpQglVPBrWzz +/2NKvV9tsQq+lISpoewliGawL6Vh7X8qyvzad1LmofEp0p9RrUwl/iGveCR0bdX1 +1D5P/JyetN97OmQoLGmI9FWWrCZ46rlqMuH6+53iejXDUGXOnn0D27y3MdmFtklI +MOMNapEt5Bz7iFbtA0uxPBD6TjlYwP5l6FEzjVyE/KeOyv+oVZzTFsKW2mfyfPrj +2N+DyPrMknZzI2joNGUkKfcuRcO681OU6tX3bj01sTWo7F6wg4ZQdT6Aje2ryScj +24eHAjbGRqI7VQGvDx/kYmSXdrbhHG4Hnmk6mhG5NO1kR6Wu8EioJ2wCjfxBmzHG +u4RP2+pWji8KIVeIy3LYfyKpgP8jxx62VbGYrUV4Iag+OPBukfe3QLhr3p5iMnoP +I0ihwNYmCzCvJ76IKLmfjwchvT/W0a29syQuKcNaofjSihIlUs3ug1T/ZG3Xyl7N +F2h0YFHnF7Rpm3WRSZQsePbMGmwiEck0UFP/Z/DPFe1V8lkH3hEty4NtMUqtn5F1 +VHalFs4C5UYKHtBRuZvqjvr0xnbZDFouCc7m4VjplzsS1tSjLl6EAyonAbbQ3vCt +Tn3v9k8Ro8J1p9mRHA1NdBCTI9J8Lkml+f1IIn8gVdHTTiYcqDxM/zRPmC5ve6fA +D+BFh7qvDkIGqpB0FIkHnstjp2/XFaxJ1Xso7e0QYo30tzVyBFNu8KQF8Okeh2Mz +6M2oKn8GInjeDMWD9DxlZwHOnlcWuXFeurj79G5YiUffg9el18KCwIMrnDD48xUq +v1gYDRy/1LAp3AE3nnm5cIPq1Rp4Nsi8DQBOW/ZLJ7HW1IXs6o2U7Hytlx+KLZ2a +3ETlZ5Nu86OwZYhErsLxDd3wF/85NYUzMxvO5uAVO2LO54jKjpTkQIHpMC4n4laH +M3bQJf2HqJIOwEdL44M5vcXV8jgfoKgoVqUlJDP9SBUQZ8aoLWCq9fAaDgjCjh38 +62zrcfclfNlAIFnje1bWz7S+Wr6IkG1vkj7ViGLix2+40wftuzo1+igIutUyRdHN +7I+ggoFBf+zXEbSlMhV6sW5/4PFw//n0+julq35FKj5psG8/XmLZun2E0CMOTNDJ +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 +AAAAAAAAAAAACgwOExkk +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc new file mode 100644 index 0000000000..577ea31841 --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc @@ -0,0 +1,292 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +QkebRJCZxZG/RCVh66PXOpv3JKQY2VscDzf7x22FDxuCDiKR72Mi15XydCi6fnQI +ug1/j6olqArvt51WpBoM/aN5uD7fuzqcv5OaCXuhyck6lhLRfp0/YRaYdlciihOb +1jHtdO4545qpeb/qETayE7R88zrUQLkiNsjdGdxHgs+MHl64KFC3D6mDvkqnFdqb +7Nd4FxDCBGv5ktQlKFKhGWtrR7PWnOYif6JqzzgsxWnC1nmVEAeWtw17s4tuke4c +Rh5owUNJBZZygKBJsn37H5cXoZHw8uzmESNxgMxw9zVUoeOc+l4eX5ynM+FxmXhx +IhOH1sQluFb1MpQglVPBrWzz/2NKvV9tsQq+lISpoewliGawL6Vh7X8qyvzad1Lm +ofEp0p9RrUwl/iGveCR0bdX11D5P/JyetN97OmQoLGmI9FWWrCZ46rlqMuH6+53i +ejXDUGXOnn0D27y3MdmFtklIMOMNapEt5Bz7iFbtA0uxPBD6TjlYwP5l6FEzjVyE +/KeOyv+oVZzTFsKW2mfyfPrj2N+DyPrMknZzI2joNGUkKfcuRcO681OU6tX3bj01 +sTWo7F6wg4ZQdT6Aje2ryScj24eHAjbGRqI7VQGvDx/kYmSXdrbhHG4Hnmk6mhG5 +NO1kR6Wu8EioJ2wCjfxBmzHGu4RP2+pWji8KIVeIy3LYfyKpgP8jxx62VbGYrUV4 +Iag+OPBukfe3QLhr3p5iMnoPI0ihwNYmCzCvJ76IKLmfjwchvT/W0a29syQuKcNa +ofjSihIlUs3ug1T/ZG3Xyl7NF2h0YFHnF7Rpm3WRSZQsePbMGmwiEck0UFP/Z/DP +Fe1V8lkH3hEty4NtMUqtn5F1VHalFs4C5UYKHtBRuZvqjvr0xnbZDFouCc7m4Vjp +lzsS1tSjLl6EAyonAbbQ3vCtTn3v9k8Ro8J1p9mRHA1NdBCTI9J8Lkml+f1IIn8g +VdHTTiYcqDxM/zRPmC5ve6fAD+BFh7qvDkIGqpB0FIkHnstjp2/XFaxJ1Xso7e0Q +Yo30tzVyBFNu8KQF8Okeh2Mz6M2oKn8GInjeDMWD9DxlZwHOnlcWuXFeurj79G5Y +iUffg9el18KCwIMrnDD48xUqv1gYDRy/1LAp3AE3nnm5cIPq1Rp4Nsi8DQBOW/ZL +J7HW1IXs6o2U7Hytlx+KLZ2a3ETlZ5Nu86OwZYhErsLxDd3wF/85NYUzMxvO5uAV +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 +-----END PGP PRIVATE KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-seipdv2.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-seipdv2.asc deleted file mode 100644 index 5c802b2a9d..0000000000 --- a/src/tests/data/draft-ietf-openpgp-pqc/v6-seipdv2.asc +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN PGP MESSAGE----- - -wcPtBiEGJj40tpk451PcZ8qO43ZSeVE14OFuSIhxA8EdcwffQO1pU4rRGXPhivmf -yaE7whd94FIPayIJxbUXuq6Ei6VifzlPu9BoxvQYZa/u+exaOVT2MLxbCAceHYMw -zSuUa0BoiugafZWAnVrn4ji+mI+298c93Ij83yUjkzvBsKyJuhesTevSpJAnjiMt -m9Mmwzc8Y9tB4N/Am8jR3p8UYLH+2aH9FyT6VdqsETYiPFcz5jZqkag7bAB88KUg -heJAHU/FgHXtz013tnPyQPtuVHeJrP8jcd3IENJh9CfSg9rkhAoW72GiSGYPm+Im -I7faHJ8LMCx4Sbur9rWsZM5Gl+sG2MyjM3SrykAfL8W6s5o3LVw+06h5XnmsLUFE -8nZf99LBm/5aUzkdmq1G8hgLY+dVhTAm1DZxbNz5nDbtCsuIbJ2knS2yqxlQV8qx -5eikfWlW29TfREHJf4von2xR668vYwOibA5sTa3utBpNk2YNalZ61p9MxPW54PvO -+EIRYVTlwhSbETanOBms+yDq0NLooO+OQerT3WA1VzeT7j5rzaQM0ULAK6x/KqKt -ARZmAIufbPktMfX26tczv5sKb5rx1MJexNUhnxvn/xGDkAvbf8HrU3BpxbqPPX6/ -MWGpggb08U8YQU8qLCMzfPaFajTfyyhOKnoh5MBLNVDzbWyqKLzLGQTXl13WSvxQ -ib2U0YoGZtVmshmiVijCu4x1aX4A02sYKrzeYP2WpAl1sEynKz1tkWZQKS3Jt+RP -Y702QtUrRWWul23cBsqy0uKcQ+Zk/rCh5qyQ9mZtx8EmathdnHpi0qZLAH4wEYTA -HScy59zUR5CoYnJNBDNyRyEuh/B/pkTGEisLb+i1F9M0WhrgfgYSldEzUCZ7KJUD -Bt/d50OM+M8kmkYYBmiyWBfNqMMckJHhjFAp8kd0V0FP7lVwPGywK2hGKQBF+ylh -9O3qq1knDkjyCLNIoi7kJkl5KEOq1qLfwRnqfTbivGnhMdWh658WX7Q5QCUZf6vT -+aH2SXA+HNd4VAQguWbnmRNmjW33PTdte25T36qHQ6jlCdyG4VZU8f1yuqzhbHPx -FwAaChb/B6l5SIxK8zupbT3K5NDsSrD0wBV52P377GGVF1z1wI/+0ftNTxu/mgx5 -aDjNyxUpupVsqWnws7a/6ixMUqyYAI8TlVWj2vBXgIg9gvt+yp/X7iMD3V4v3z/P -c8o2nH1c9uB6bXJx4HrtenjkDA4uQ89NH35yBII5OviL4NWTmMArWSN5scBOYOMJ -KxGDvFCkgcGjE5x6srj0gzntFg3TcYQiC4+onS485xTQlud53Z7Vc4JnSn7F2j9P -AJX5s+yUgT+ob7SYvYigbp30Oe5fg0NVZzH1fJ3eTJheClb+87vjrZS4X1tVU0MG -LNCdjF20Jff31fZu16mxin+hUZVyfVRlSycfVJU7qXzZftTpr3rsNZiLlY7z7BRe -1lXSx7uZFCfhyu96hyIPxMuaBIX7OHkduinurQ58Vj92fAe/8f5jviRVqtKwZ9Ao -lZfjSn8k/CgDxzFu2gWpbhWJ4GKvoXgmWDESk4o07UGqDbhm0YO5MDM0uoAmsGae -0lQCCQIMTzhupJq1yZ3vljrXBYF37QuWDi/MAvL54hMis5UXrTAh0+FobZAXCoO0 -epU7H8CIs4ZCelIyD1W+K/kv3/E3Z65WQzDOYOhXSCAOqcpjiPc= ------END PGP MESSAGE----- diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp index 4889834ba1..1f52d706eb 100644 --- a/src/tests/ffi-enc.cpp +++ b/src/tests/ffi-enc.cpp @@ -915,18 +915,17 @@ TEST_F(rnp_tests, test_ffi_decrypt_v6_pkesk_test_vector) #if defined(ENABLE_PQC) TEST_F(rnp_tests, test_ffi_decrypt_pqc_pkesk_test_vector) { - return; // TODO: need new test vectors rnp_ffi_t ffi = NULL; rnp_input_t input = NULL; rnp_output_t output = NULL; assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_true(import_all_keys(ffi, "data/draft-ietf-openpgp-pqc/v6-eddsa-mlkem.sec.asc")); - assert_true(import_all_keys(ffi, "data/draft-ietf-openpgp-pqc/v4-eddsa-mlkem.sec.asc")); + assert_true(import_all_keys(ffi, "data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc")); + assert_true(import_all_keys(ffi, "data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc")); assert_rnp_success(rnp_output_to_path(&output, "decrypted")); assert_rnp_success( - rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-seipdv2.asc")); + rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message.asc")); assert_non_null(input); assert_rnp_success(rnp_decrypt(ffi, input, output)); assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); @@ -936,7 +935,7 @@ TEST_F(rnp_tests, test_ffi_decrypt_pqc_pkesk_test_vector) assert_rnp_success(rnp_output_to_path(&output, "decrypted")); assert_rnp_success( - rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v4-seipdv1.asc")); + rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-mldsa-sample-message.asc")); assert_non_null(input); assert_rnp_success(rnp_decrypt(ffi, input, output)); assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); @@ -944,17 +943,6 @@ TEST_F(rnp_tests, test_ffi_decrypt_pqc_pkesk_test_vector) rnp_input_destroy(input); rnp_output_destroy(output); - assert_rnp_success(rnp_output_to_path(&output, "decrypted")); - assert_rnp_success( - rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v4-seipdv1.asc")); - assert_non_null(input); - assert_rnp_success(rnp_decrypt(ffi, input, output)); - assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); - - assert_int_equal(unlink("decrypted"), 0); - rnp_input_destroy(input); - rnp_output_destroy(output); - rnp_ffi_destroy(ffi); } diff --git a/src/tests/ffi-key.cpp b/src/tests/ffi-key.cpp index 4b51a2437f..5090c23b1b 100644 --- a/src/tests/ffi-key.cpp +++ b/src/tests/ffi-key.cpp @@ -3252,16 +3252,44 @@ TEST_F(rnp_tests, test_ffi_v6_cert_import) #if defined(ENABLE_PQC) TEST_F(rnp_tests, test_ffi_pqc_certs) { - return; // TODO: need new test vectors - rnp_ffi_t ffi = NULL; rnp_input_t input = NULL; size_t keycount = 255; - /* Public Key */ + /* Public Key 1 */ + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success( + rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-eddsa-sample-pk.asc")); + assert_rnp_success( + rnp_import_keys(ffi, + input, + RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SINGLE | RNP_LOAD_SAVE_BASE64, + NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 2); + assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); + assert_int_equal(keycount, 0); + rnp_ffi_destroy(ffi); + + /* Private Key 1 */ + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success( + rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc")); + assert_rnp_success( + rnp_import_keys(ffi, + input, + RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_SINGLE | RNP_LOAD_SAVE_BASE64, + NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); + assert_int_equal(keycount, 2); + rnp_ffi_destroy(ffi); + + /* Public Key 2 */ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); assert_rnp_success( - rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-eddsa-mlkem.pub.asc")); + rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-mldsa-sample-pk.asc")); assert_rnp_success( rnp_import_keys(ffi, input, @@ -3274,10 +3302,10 @@ TEST_F(rnp_tests, test_ffi_pqc_certs) assert_int_equal(keycount, 0); rnp_ffi_destroy(ffi); - /* Private Key */ + /* Private Key 2 */ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); assert_rnp_success( - rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-eddsa-mlkem.sec.asc")); + rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc")); assert_rnp_success( rnp_import_keys(ffi, input, From 017efb7ecd8c28d4a2b6fe9518103c7caf2ba38f Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Tue, 29 Oct 2024 19:37:27 +0100 Subject: [PATCH 10/15] fix v6 signature salt and add some test vectors fail gracefully on parsing v6 cleartext sigs --- include/repgp/repgp_def.h | 5 -- src/lib/crypto/hash.hpp | 13 +++- src/lib/crypto/hash_common.cpp | 44 ++++++++++- src/lib/pgp-key.cpp | 11 +-- src/librepgp/stream-packet.cpp | 4 +- src/librepgp/stream-parse.cpp | 48 +++++++++++- src/librepgp/stream-sig.cpp | 13 ++-- src/librepgp/stream-sig.h | 3 +- src/librepgp/stream-write.cpp | 76 +++++++++++++------ .../v6-eddsa-sample-message | 1 + .../a6-sample-cleartext-signed-message.asc | 15 ++++ .../a7-sample-inline-signed-message.asc | 10 +++ src/tests/ffi-enc.cpp | 63 +++++++++++++++ 13 files changed, 255 insertions(+), 51 deletions(-) create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message create mode 100644 src/tests/data/test_v6_valid_data/a6-sample-cleartext-signed-message.asc create mode 100644 src/tests/data/test_v6_valid_data/a7-sample-inline-signed-message.asc diff --git a/include/repgp/repgp_def.h b/include/repgp/repgp_def.h index 3458034342..82823663c5 100644 --- a/include/repgp/repgp_def.h +++ b/include/repgp/repgp_def.h @@ -121,11 +121,6 @@ static_assert(PGP_FINGERPRINT_V5_SIZE == PGP_FINGERPRINT_V6_SIZE, "FP size misma #define PGP_MARKER_CONTENTS "PGP" #define PGP_MARKER_LEN 3 -/* V6 Signature Salt */ -#if defined(ENABLE_CRYPTO_REFRESH) -#define PGP_MAX_SALT_SIZE_V6_SIG 32 -#endif - /** Old Packet Format Lengths. * Defines the meanings of the 2 bits for length type in the * old packet format. diff --git a/src/lib/crypto/hash.hpp b/src/lib/crypto/hash.hpp index c771302e9d..cbed29a9ca 100644 --- a/src/lib/crypto/hash.hpp +++ b/src/lib/crypto/hash.hpp @@ -87,11 +87,20 @@ class CRC24 { class HashList { public: - std::vector> hashes; + void add(const void *buf, size_t len); + std::vector hash_algs() const; + /* v3-v5 sigs */ void add_alg(pgp_hash_alg_t alg); const Hash *get(pgp_hash_alg_t alg) const; - void add(const void *buf, size_t len); + +#if defined(ENABLE_CRYPTO_REFRESH) + /* v6 sigs with salt */ + void add_alg(pgp_hash_alg_t alg, std::vector salt); + const Hash *get(pgp_hash_alg_t alg, std::vector salt) const; +#endif + + std::vector, std::vector>> hashes; }; } // namespace rnp diff --git a/src/lib/crypto/hash_common.cpp b/src/lib/crypto/hash_common.cpp index 92a4d01c39..4ea5f67876 100644 --- a/src/lib/crypto/hash_common.cpp +++ b/src/lib/crypto/hash_common.cpp @@ -37,6 +37,7 @@ #include "hash_ossl.hpp" #include "hash_crc24.hpp" #endif +#include static const struct hash_alg_map_t { pgp_hash_alg_t type; @@ -174,7 +175,7 @@ void HashList::add_alg(pgp_hash_alg_t alg) { if (!get(alg)) { - hashes.emplace_back(rnp::Hash::create(alg)); + hashes.emplace_back(std::make_pair(rnp::Hash::create(alg), std::vector())); } } @@ -182,8 +183,8 @@ const Hash * HashList::get(pgp_hash_alg_t alg) const { for (auto &hash : hashes) { - if (hash->alg() == alg) { - return hash.get(); + if (hash.first->alg() == alg && hash.second.empty()) { + return hash.first.get(); } } return NULL; @@ -193,8 +194,43 @@ void HashList::add(const void *buf, size_t len) { for (auto &hash : hashes) { - hash->add(buf, len); + hash.first->add(buf, len); } } +std::vector +HashList::hash_algs() const +{ + std::set algs; + for (auto &hash : hashes) { + algs.insert(hash.first->alg()); + } + + std::vector result; + std::copy(algs.begin(), algs.end(), std::back_inserter(result)); + return result; +} + +#if defined(ENABLE_CRYPTO_REFRESH) +void +HashList::add_alg(pgp_hash_alg_t alg, std::vector salt) +{ + if (!get(alg, salt)) { + hashes.emplace_back(std::make_pair(rnp::Hash::create(alg), salt)); + hashes.back().first->add(salt); // consume salt as first input + } +} + +const Hash * +HashList::get(pgp_hash_alg_t alg, std::vector salt) const +{ + for (auto &hash : hashes) { + if (hash.first->alg() == alg && hash.second == salt) { + return hash.first.get(); + } + } + return NULL; +} +#endif + } // namespace rnp diff --git a/src/lib/pgp-key.cpp b/src/lib/pgp-key.cpp index 0103e13cfa..51e2b0da07 100644 --- a/src/lib/pgp-key.cpp +++ b/src/lib/pgp-key.cpp @@ -2405,11 +2405,12 @@ pgp_key_t::sign_init(rnp::RNG & rng, sig.set_keyid(keyid()); } #if defined(ENABLE_CRYPTO_REFRESH) - if (version == PGP_V6) { - size_t tmp_salt_size; - assert(pgp_signature_t::v6_salt_size(sig.halg, &tmp_salt_size)); - sig.salt_size = tmp_salt_size; - rng.get(sig.salt, sig.salt_size); + if (version == PGP_V6 && sig.salt.empty()) { + /* salt is either set (OPS, etc) or empty (key sigs) and needs to be set here */ + size_t salt_size; + assert(pgp_signature_t::v6_salt_size(sig.halg, &salt_size)); + sig.salt.resize(salt_size); + rng.get(sig.salt.data(), salt_size); } #endif } diff --git a/src/librepgp/stream-packet.cpp b/src/librepgp/stream-packet.cpp index 68c8cf8ca4..a30be192a8 100644 --- a/src/librepgp/stream-packet.cpp +++ b/src/librepgp/stream-packet.cpp @@ -1490,10 +1490,10 @@ pgp_one_pass_sig_t::parse(pgp_source_t &src) rnp_result_t pgp_one_pass_sig_t::parse_v6(pgp_packet_body_t &pkt) { - uint8_t buf[32]; + uint8_t buf[4]; uint8_t salt_size; - /* packet can't be smaller fo v6 */ + /* packet can't be smaller for v6 */ const size_t min_size = 54; if ((pkt.size() < min_size)) { return RNP_ERROR_BAD_FORMAT; diff --git a/src/librepgp/stream-parse.cpp b/src/librepgp/stream-parse.cpp index 75df9b2c03..1d6c32f21c 100644 --- a/src/librepgp/stream-parse.cpp +++ b/src/librepgp/stream-parse.cpp @@ -857,10 +857,33 @@ add_hash_for_sig(pgp_source_signed_param_t *param, pgp_sig_type_t stype, pgp_has param->hashes.add_alg(halg); } +#if defined(ENABLE_CRYPTO_REFRESH) +static void +add_hash_for_sig_v6(pgp_source_signed_param_t *param, + pgp_sig_type_t stype, + pgp_hash_alg_t halg, + std::vector salt) +{ + /* Cleartext always uses param->hashes instead of param->txt_hashes */ + if (!param->cleartext && (stype == PGP_SIG_TEXT)) { + param->txt_hashes.add_alg(halg, salt); + } + param->hashes.add_alg(halg, salt); +} +#endif + static const rnp::Hash * get_hash_for_sig(pgp_source_signed_param_t ¶m, pgp_signature_info_t &sinfo) { /* Cleartext always uses param->hashes instead of param->txt_hashes */ +#if defined(ENABLE_CRYPTO_REFRESH) + if (sinfo.sig->version == PGP_V6) { + if (!param.cleartext && (sinfo.sig->type() == PGP_SIG_TEXT)) { + return param.txt_hashes.get(sinfo.sig->halg, sinfo.sig->salt); + } + return param.hashes.get(sinfo.sig->halg, sinfo.sig->salt); + } +#endif if (!param.cleartext && (sinfo.sig->type() == PGP_SIG_TEXT)) { return param.txt_hashes.get(sinfo.sig->halg); } @@ -1178,6 +1201,13 @@ signed_src_finish(pgp_source_t *src) if (!sinfo.sig) { continue; } +#if defined(ENABLE_CRYPTO_REFRESH) + if (sinfo.sig->version == PGP_V6 && param->cleartext) { + RNP_LOG("Skipping signature: Cleartext signature verification currently does not " + "support v6 signatures"); + continue; + } +#endif signed_validate_signature(*param, sinfo); } @@ -2603,7 +2633,14 @@ init_signed_src(pgp_parse_handler_t *handler, pgp_source_t *src, pgp_source_t *r /* adding hash context */ try { - add_hash_for_sig(param, onepass.type, onepass.halg); +#if defined(ENABLE_CRYPTO_REFRESH) + if (onepass.version == PGP_OPS_V6) { + add_hash_for_sig_v6(param, onepass.type, onepass.halg, onepass.salt); + } else +#endif + { + add_hash_for_sig(param, onepass.type, onepass.halg); + } } catch (const std::exception &e) { RNP_LOG("Failed to create hash %d for onepass %d : %s.", (int) onepass.halg, @@ -2625,7 +2662,14 @@ init_signed_src(pgp_parse_handler_t *handler, pgp_source_t *src, pgp_source_t *r /* adding hash context */ if (sig) { try { - add_hash_for_sig(param, sig->type(), sig->halg); +#if defined(ENABLE_CRYPTO_REFRESH) + if (sig->version == PGP_V6) { + add_hash_for_sig_v6(param, sig->type(), sig->halg, sig->salt); + } else +#endif + { + add_hash_for_sig(param, sig->type(), sig->halg); + } } catch (const std::exception &e) { RNP_LOG("Failed to create hash %d for sig %d : %s.", (int) sig->halg, diff --git a/src/librepgp/stream-sig.cpp b/src/librepgp/stream-sig.cpp index 3702f96266..e7008755b2 100644 --- a/src/librepgp/stream-sig.cpp +++ b/src/librepgp/stream-sig.cpp @@ -740,6 +740,10 @@ pgp_signature_t::matches_onepass(const pgp_one_pass_sig_t &onepass) const return false; } } + /* check keyid (V3) */ + if (onepass.version == PGP_OPS_V3 && (onepass.keyid != keyid())) { + return false; + } #if defined(ENABLE_CRYPTO_REFRESH) /* checks for V6 */ if (onepass.version == PGP_OPS_V6) { @@ -751,16 +755,12 @@ pgp_signature_t::matches_onepass(const pgp_one_pass_sig_t &onepass) const if (onepass.fp != keyfp()) { return false; } - /* check salt (V6) */ + /* check salt */ if (onepass.salt != salt) { return false; } } #endif - /* check keyid (V3) */ - if (onepass.version == PGP_OPS_V3 && (onepass.keyid != keyid())) { - return false; - } /* check the remaining common attributes */ return (halg == onepass.halg) && (palg == onepass.palg) && (type_ == onepass.type); } @@ -1039,7 +1039,8 @@ pgp_signature_t::parse(pgp_packet_body_t &pkt) RNP_LOG("invalid salt size"); return RNP_ERROR_BAD_FORMAT; } - if (!pkt.get(salt, salt_size)) { + salt.resize(salt_size); + if (!pkt.get(salt.data(), salt_size)) { RNP_LOG("not enough data for v6 signature salt"); return RNP_ERROR_BAD_FORMAT; } diff --git a/src/librepgp/stream-sig.h b/src/librepgp/stream-sig.h index 48fbc541d4..8a35386f60 100644 --- a/src/librepgp/stream-sig.h +++ b/src/librepgp/stream-sig.h @@ -66,8 +66,7 @@ typedef struct pgp_signature_t { #if defined(ENABLE_CRYPTO_REFRESH) /* v6 - only fields */ - uint8_t salt[PGP_MAX_SALT_SIZE_V6_SIG]; - uint8_t salt_size; + std::vector salt; /* maps halg to V6 salt size. Returns false if no mapping exists. */ static bool v6_salt_size(pgp_hash_alg_t halg, size_t *salt_size); diff --git a/src/librepgp/stream-write.cpp b/src/librepgp/stream-write.cpp index a0939b2dc6..7233d67281 100644 --- a/src/librepgp/stream-write.cpp +++ b/src/librepgp/stream-write.cpp @@ -138,6 +138,9 @@ typedef struct pgp_dest_signer_info_t { pgp_hash_alg_t halg; int64_t sigcreate; uint64_t sigexpire; +#if defined(ENABLE_CRYPTO_REFRESH) + std::vector salt; +#endif } pgp_dest_signer_info_t; typedef struct pgp_dest_signed_param_t { @@ -1257,7 +1260,16 @@ signed_fill_signature(pgp_dest_signed_param_t ¶m, sig.set_expiration(signer.sigexpire); sig.fill_hashed_data(); - auto listh = param.hashes.get(sig.halg); + const rnp::Hash *listh = NULL; +#if defined(ENABLE_CRYPTO_REFRESH) + if (sig.version == PGP_V6) { + listh = param.hashes.get(sig.halg, sig.salt); + } else +#endif + { + listh = param.hashes.get(sig.halg); + } + if (!listh) { /* LCOV_EXCL_START */ RNP_LOG("failed to obtain hash"); @@ -1285,6 +1297,12 @@ signed_write_signature(pgp_dest_signed_param_t *param, { try { pgp_signature_t sig; +#if defined(ENABLE_CRYPTO_REFRESH) + if (signer->key->version() == PGP_V6) { + // set salt + sig.salt = signer->salt; + } +#endif if (signer->onepass.version) { signer->key->sign_init(param->ctx->ctx->rng, sig, @@ -1405,7 +1423,10 @@ signed_dst_set_literal_hdr(pgp_dest_t &src, const pgp_literal_hdr_t &hdr) } static rnp_result_t -signed_add_signer(pgp_dest_signed_param_t *param, rnp_signer_info_t *signer, bool last) +signed_add_signer(pgp_dest_signed_param_t *param, + rnp_signer_info_t * signer, + rnp::RNG & rng, + bool last) { pgp_dest_signer_info_t sinfo = {}; @@ -1428,7 +1449,21 @@ signed_add_signer(pgp_dest_signed_param_t *param, rnp_signer_info_t *signer, boo /* Add hash to the list */ sinfo.halg = signer->key->material()->adjust_hash(signer->halg); try { - param->hashes.add_alg(sinfo.halg); +#if defined(ENABLE_CRYPTO_REFRESH) + if (sinfo.key->version() == PGP_V6) { + size_t salt_size; + if (!pgp_signature_t::v6_salt_size(sinfo.halg, &salt_size)) { + RNP_LOG("can't get signature salt size"); + return RNP_ERROR_BAD_STATE; + } + sinfo.salt.resize(salt_size); + rng.get(sinfo.salt.data(), salt_size); + param->hashes.add_alg(sinfo.halg, sinfo.salt); + } else +#endif + { + param->hashes.add_alg(sinfo.halg); + } } catch (const std::exception &e) { /* LCOV_EXCL_START */ RNP_LOG("%s", e.what()); @@ -1450,22 +1485,6 @@ signed_add_signer(pgp_dest_signed_param_t *param, rnp_signer_info_t *signer, boo } } -#if defined(ENABLE_CRYPTO_REFRESH) - // Do not create OPS for V6 (currently not implemented) - if (sinfo.key->version() == PGP_V6) { - sinfo.onepass.version = 0; - try { - param->siginfos.push_back(sinfo); - return RNP_SUCCESS; - } catch (const std::exception &e) { - /* LCOV_EXCL_START */ - RNP_LOG("%s", e.what()); - return RNP_ERROR_OUT_OF_MEMORY; - /* LCOV_EXCL_END */ - } - } -#endif - // Setup and add onepass sinfo.onepass.version = 3; sinfo.onepass.type = PGP_SIG_BINARY; @@ -1473,6 +1492,15 @@ signed_add_signer(pgp_dest_signed_param_t *param, rnp_signer_info_t *signer, boo sinfo.onepass.palg = sinfo.key->alg(); sinfo.onepass.keyid = sinfo.key->keyid(); sinfo.onepass.nested = false; +#if defined(ENABLE_CRYPTO_REFRESH) + if (sinfo.key->version() == PGP_V6) { + // set version and fingerprint for v6 + // salt already set above + sinfo.onepass.version = (uint8_t) PGP_OPS_V6; + sinfo.onepass.salt = sinfo.salt; + sinfo.onepass.fp = sinfo.key->fp(); + } +#endif try { param->siginfos.push_back(sinfo); } catch (const std::exception &e) { @@ -1541,7 +1569,8 @@ init_signed_dst(pgp_write_handler_t *handler, pgp_dest_t *dst, pgp_dest_t *write /* Getting signer's infos, writing one-pass signatures if needed */ for (auto &sg : handler->ctx->signers) { - ret = signed_add_signer(param, &sg, &sg == &handler->ctx->signers.back()); + ret = signed_add_signer( + param, &sg, param->ctx->ctx->rng, &sg == &handler->ctx->signers.back()); if (ret) { RNP_LOG("failed to add one-pass signature for signer"); goto finish; @@ -1560,10 +1589,11 @@ init_signed_dst(pgp_write_handler_t *handler, pgp_dest_t *dst, pgp_dest_t *write dst_write(param->writedst, ST_CRLF, strlen(ST_CRLF)); dst_write(param->writedst, ST_HEADER_HASH, strlen(ST_HEADER_HASH)); - for (const auto &hash : param->hashes.hashes) { - auto hname = rnp::Hash::name(hash->alg()); + auto halg_list = param->hashes.hash_algs(); + for (pgp_hash_alg_t halg : halg_list) { + auto hname = rnp::Hash::name(halg); dst_write(param->writedst, hname, strlen(hname)); - if (&hash != ¶m->hashes.hashes.back()) { + if (halg != halg_list.back()) { dst_write(param->writedst, ST_COMMA, 1); } } diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message new file mode 100644 index 0000000000..73709ba686 --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message @@ -0,0 +1 @@ +Testing diff --git a/src/tests/data/test_v6_valid_data/a6-sample-cleartext-signed-message.asc b/src/tests/data/test_v6_valid_data/a6-sample-cleartext-signed-message.asc new file mode 100644 index 0000000000..958bc382d3 --- /dev/null +++ b/src/tests/data/test_v6_valid_data/a6-sample-cleartext-signed-message.asc @@ -0,0 +1,15 @@ +-----BEGIN PGP SIGNED MESSAGE----- + +What we need from the grocery store: + +- - tofu +- - vegetables +- - noodles + +-----BEGIN PGP SIGNATURE----- + +wpgGARsKAAAAKQWCY5ijYyIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAGk2IHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usJ9BvuAqo +/FvLFuGWMbKAdA+epq7V4HOtAPlBWmU8QOd6aud+aSunHQaaEJ+iTFjP2OMW0KBr +NK2ay45cX1IVAQ== +-----END PGP SIGNATURE----- diff --git a/src/tests/data/test_v6_valid_data/a7-sample-inline-signed-message.asc b/src/tests/data/test_v6_valid_data/a7-sample-inline-signed-message.asc new file mode 100644 index 0000000000..627a98a704 --- /dev/null +++ b/src/tests/data/test_v6_valid_data/a7-sample-inline-signed-message.asc @@ -0,0 +1,10 @@ +-----BEGIN PGP MESSAGE----- + +xEYGAQobIHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usyxhsTwYJppfk +1S36bHIrDB8eJ8GKVnCPZSXsJ7rZrMkBy0p1AAAAAABXaGF0IHdlIG5lZWQgZnJv +bSB0aGUgZ3JvY2VyeSBzdG9yZToKCi0gdG9mdQotIHZlZ2V0YWJsZXMKLSBub29k +bGVzCsKYBgEbCgAAACkFgmOYo2MiIQbLGGxPBgmml+TVLfpscisMHx4nwYpWcI9l +JewnutmsyQAAAABpNiB2SV9QIYiQ9/Xi7jwYIlFPcFAPVR2G5ckh5ATjSlP7rCfQ +b7gKqPxbyxbhljGygHQPnqau1eBzrQD5QVplPEDnemrnfmkrpx0GmhCfokxYz9jj +FtCgazStmsuOXF9SFQE= +-----END PGP MESSAGE----- diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp index 1f52d706eb..1ac7940375 100644 --- a/src/tests/ffi-enc.cpp +++ b/src/tests/ffi-enc.cpp @@ -912,6 +912,69 @@ TEST_F(rnp_tests, test_ffi_decrypt_v6_pkesk_test_vector) rnp_ffi_destroy(ffi); } +TEST_F(rnp_tests, test_ffi_verify_v2_seipd_test_vector) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_verify_t verify = NULL; + + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_true(import_all_keys(ffi, "data/test_v6_valid_data/transferable_seckey_v6.asc")); + + assert_rnp_success(rnp_input_from_path( + &input, "data/test_v6_valid_data/a7-sample-inline-signed-message.asc")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + + /* dash-escaped */ + assert_string_equal( + file_to_str("decrypted").c_str(), + "What we need from the grocery store:\n\n- tofu\n- vegetables\n- noodles\n"); + assert_int_equal(unlink("decrypted"), 0); + + // cleanup + rnp_input_destroy(input); + rnp_output_destroy(output); + rnp_op_verify_destroy(verify); + rnp_ffi_destroy(ffi); +} + +/* RNP currently skips v6 signatures on cleartext signatures. +This test expects the failure. Eventually, if v6 signatures are supported, the test should be +adapted */ +TEST_F(rnp_tests, test_ffi_verify_v2_seipd_cleartext_test_vector) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_verify_t verify = NULL; + + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_true(import_all_keys(ffi, "data/test_v6_valid_data/transferable_seckey_v6.asc")); + + assert_rnp_success(rnp_input_from_path( + &input, "data/test_v6_valid_data/a6-sample-cleartext-signed-message.asc")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_int_equal(RNP_ERROR_SIGNATURE_INVALID, rnp_op_verify_execute(verify)); + + // /* dash-escaped */ + // assert_string_equal( + // file_to_str("decrypted").c_str(), + // "What we need from the grocery store:\n\n- tofu\n- vegetables\n- noodles\n"); + // assert_int_equal(unlink("decrypted"), 0); + + // cleanup + rnp_input_destroy(input); + rnp_output_destroy(output); + rnp_op_verify_destroy(verify); + rnp_ffi_destroy(ffi); +} + #if defined(ENABLE_PQC) TEST_F(rnp_tests, test_ffi_decrypt_pqc_pkesk_test_vector) { From eb55b4c8af464ae1fc75120657b93e24c1f998e7 Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Wed, 30 Oct 2024 16:38:26 +0100 Subject: [PATCH 11/15] Some cleanup and refactoring --- src/lib/CMakeLists.txt | 21 ++++++++----------- src/lib/crypto/common.h | 4 ++-- src/lib/crypto/ec.cpp | 8 +++---- src/lib/crypto/ec.h | 2 +- .../crypto/{ed25519.cpp => ed25519_ed448.cpp} | 2 +- src/lib/crypto/{ed25519.h => ed25519_ed448.h} | 0 src/lib/crypto/exdsa_ecdhkem.cpp | 2 +- src/lib/crypto/rng.cpp | 4 ++-- src/lib/crypto/rng.h | 6 +++--- .../crypto/{x25519.cpp => x25519_x448.cpp} | 2 +- src/lib/crypto/{x25519.h => x25519_x448.h} | 0 src/lib/generate-key.cpp | 2 +- src/librepgp/stream-common.cpp | 2 +- src/librepgp/stream-common.h | 2 +- src/librepgp/stream-dump.cpp | 2 +- src/librepgp/stream-key.cpp | 2 +- src/librepgp/stream-parse.cpp | 14 ++++++------- src/librepgp/stream-write.cpp | 8 +++---- .../A.3.transferable-pubkey-v6.asc} | 0 .../A.4.transferable-seckey-v6.asc} | 0 .../A.6.-sample-cleartext-signed-message.asc} | 0 .../A.7.-sample-inline-signed-message.asc} | 0 .../A.8.5-v6pkesk-v2seipd} | 0 src/tests/exdsa_ecdhkem.cpp | 2 +- src/tests/ffi-enc.cpp | 14 ++++++------- src/tests/ffi-key.cpp | 4 ++-- 26 files changed, 49 insertions(+), 54 deletions(-) rename src/lib/crypto/{ed25519.cpp => ed25519_ed448.cpp} (99%) rename src/lib/crypto/{ed25519.h => ed25519_ed448.h} (100%) rename src/lib/crypto/{x25519.cpp => x25519_x448.cpp} (99%) rename src/lib/crypto/{x25519.h => x25519_x448.h} (100%) rename src/tests/data/{test_v6_valid_data/transferable_pubkey_v6.asc => RFC9580/A.3.transferable-pubkey-v6.asc} (100%) rename src/tests/data/{test_v6_valid_data/transferable_seckey_v6.asc => RFC9580/A.4.transferable-seckey-v6.asc} (100%) rename src/tests/data/{test_v6_valid_data/a6-sample-cleartext-signed-message.asc => RFC9580/A.6.-sample-cleartext-signed-message.asc} (100%) rename src/tests/data/{test_v6_valid_data/a7-sample-inline-signed-message.asc => RFC9580/A.7.-sample-inline-signed-message.asc} (100%) rename src/tests/data/{test_v6_valid_data/v6pkesk.asc => RFC9580/A.8.5-v6pkesk-v2seipd} (100%) diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 5b70f57d71..8260beb0a0 100755 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -31,10 +31,7 @@ find_package(ZLIB REQUIRED) # required packages find_package(JSON-C 0.11 REQUIRED) -if(ENABLE_PQC) - find_package(Botan 3.6.0 REQUIRED) - set(CRYPTO_BACKEND_BOTAN3 1) -elseif(ENABLE_CRYPTO_REFRESH) +if(ENABLE_CRYPTO_REFRESH) find_package(Botan 3.6.0 REQUIRED) set(CRYPTO_BACKEND_BOTAN3 1) else() @@ -59,7 +56,7 @@ if(CRYPTO_BACKEND_BOTAN3) set(CMAKE_CXX_STANDARD 20) endif() -if(ENABLE_PQC) +if(ENABLE_CRYPTO_REFRESH) if (NOT CRYPTO_BACKEND_BOTAN3) message(FATAL_ERROR "ENABLE_PQC requires Botan 3 as crypto backend") endif() @@ -69,6 +66,10 @@ 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() +# check that ENABLE_CRYPTO_REFRESH is enabled for ENABLE_PQC +if(ENABLE_PQC AND (NOT ENABLE_CRYPTO_REFRESH)) + message(FATAL_ERROR "ENABLE_PQC requires ENABLE_CRYPTO_REFRESH") +endif() # generate a config.h include(CheckIncludeFileCXX) @@ -306,14 +307,11 @@ elseif(CRYPTO_BACKEND_BOTAN) if(ENABLE_SM2) list(APPEND CRYPTO_SOURCES crypto/sm2.cpp) endif() - if(ENABLE_PQC OR ENABLE_CRYPTO_REFRESH) - list(APPEND CRYPTO_SOURCES - crypto/exdsa_ecdhkem.cpp - crypto/ed25519.cpp - crypto/x25519.cpp - ) if(ENABLE_CRYPTO_REFRESH) list(APPEND CRYPTO_SOURCES + crypto/exdsa_ecdhkem.cpp + crypto/ed25519_ed448.cpp + crypto/x25519_x448.cpp crypto/hkdf.cpp crypto/hkdf_botan.cpp ) @@ -331,7 +329,6 @@ elseif(CRYPTO_BACKEND_BOTAN) crypto/kmac_botan.cpp ) endif() - endif() else() message(FATAL_ERROR "Unknown crypto backend: ${CRYPTO_BACKEND}.") endif() diff --git a/src/lib/crypto/common.h b/src/lib/crypto/common.h index cfa73e24ed..942b6622ab 100644 --- a/src/lib/crypto/common.h +++ b/src/lib/crypto/common.h @@ -45,8 +45,8 @@ #include "sphincsplus.h" #endif #if defined(ENABLE_CRYPTO_REFRESH) -#include "x25519.h" -#include "ed25519.h" +#include "x25519_x448.h" +#include "ed25519_ed448.h" #endif /* symmetric crypto */ #include "symmetric.h" diff --git a/src/lib/crypto/ec.cpp b/src/lib/crypto/ec.cpp index 9f9723cbc2..42cc65913c 100644 --- a/src/lib/crypto/ec.cpp +++ b/src/lib/crypto/ec.cpp @@ -32,9 +32,9 @@ #include "utils.h" #include "mem.h" #include "bn.h" -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) -#include "x25519.h" -#include "ed25519.h" +#if defined(ENABLE_CRYPTO_REFRESH) +#include "x25519_x448.h" +#include "ed25519_ed448.h" #include "botan/bigint.h" #include "botan/ecdh.h" #include @@ -193,7 +193,7 @@ ec_generate(rnp::RNG * rng, return ret; } -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) static bool is_generic_prime_curve(pgp_curve_t curve) { diff --git a/src/lib/crypto/ec.h b/src/lib/crypto/ec.h index d9aa7d8115..8cd6f6f206 100644 --- a/src/lib/crypto/ec.h +++ b/src/lib/crypto/ec.h @@ -102,7 +102,7 @@ typedef struct pgp_ec_signature_t { pgp::mpi s; } pgp_ec_signature_t; -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) typedef struct pgp_ed25519_key_t { std::vector pub; // \ native encoding std::vector priv; // / diff --git a/src/lib/crypto/ed25519.cpp b/src/lib/crypto/ed25519_ed448.cpp similarity index 99% rename from src/lib/crypto/ed25519.cpp rename to src/lib/crypto/ed25519_ed448.cpp index 6ebf82415f..1e8b121eff 100644 --- a/src/lib/crypto/ed25519.cpp +++ b/src/lib/crypto/ed25519_ed448.cpp @@ -24,7 +24,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "ed25519.h" +#include "ed25519_ed448.h" #include "logging.h" #include "utils.h" diff --git a/src/lib/crypto/ed25519.h b/src/lib/crypto/ed25519_ed448.h similarity index 100% rename from src/lib/crypto/ed25519.h rename to src/lib/crypto/ed25519_ed448.h diff --git a/src/lib/crypto/exdsa_ecdhkem.cpp b/src/lib/crypto/exdsa_ecdhkem.cpp index 73b364f593..edf31a5db3 100644 --- a/src/lib/crypto/exdsa_ecdhkem.cpp +++ b/src/lib/crypto/exdsa_ecdhkem.cpp @@ -26,7 +26,7 @@ #include "exdsa_ecdhkem.h" #include "ecdh.h" -#include "ed25519.h" +#include "ed25519_ed448.h" #include "ecdsa.h" #include "ec.h" #include "types.h" diff --git a/src/lib/crypto/rng.cpp b/src/lib/crypto/rng.cpp index 50e74ec215..63a16e003f 100644 --- a/src/lib/crypto/rng.cpp +++ b/src/lib/crypto/rng.cpp @@ -35,7 +35,7 @@ RNG::RNG(Type type) if (botan_rng_init(&botan_rng, type == Type::DRBG ? "user" : NULL)) { throw rnp::rnp_exception(RNP_ERROR_RNG); } -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) if (type == Type::DRBG) { botan_rng_obj.reset(new Botan::AutoSeeded_RNG); } else { @@ -64,7 +64,7 @@ RNG::handle() return botan_rng; } -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) Botan::RandomNumberGenerator * RNG::obj() const { diff --git a/src/lib/crypto/rng.h b/src/lib/crypto/rng.h index d60dea9e69..00da52c8e7 100644 --- a/src/lib/crypto/rng.h +++ b/src/lib/crypto/rng.h @@ -35,7 +35,7 @@ #ifdef CRYPTO_BACKEND_BOTAN typedef struct botan_rng_struct *botan_rng_t; -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) #include #include #endif @@ -46,7 +46,7 @@ class RNG { private: #ifdef CRYPTO_BACKEND_BOTAN struct botan_rng_struct *botan_rng; -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) std::unique_ptr botan_rng_obj; #endif #endif @@ -80,7 +80,7 @@ class RNG { */ struct botan_rng_struct *handle(); -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) /** * @brief Returns the Botan RNG C++ object * Note: It is planned to move away from the FFI handle. diff --git a/src/lib/crypto/x25519.cpp b/src/lib/crypto/x25519_x448.cpp similarity index 99% rename from src/lib/crypto/x25519.cpp rename to src/lib/crypto/x25519_x448.cpp index f3449c7515..eb0ade3171 100644 --- a/src/lib/crypto/x25519.cpp +++ b/src/lib/crypto/x25519_x448.cpp @@ -24,7 +24,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "x25519.h" +#include "x25519_x448.h" #include #include #include "exdsa_ecdhkem.h" diff --git a/src/lib/crypto/x25519.h b/src/lib/crypto/x25519_x448.h similarity index 100% rename from src/lib/crypto/x25519.h rename to src/lib/crypto/x25519_x448.h diff --git a/src/lib/generate-key.cpp b/src/lib/generate-key.cpp index 0c31f0f31a..95379ac4f0 100644 --- a/src/lib/generate-key.cpp +++ b/src/lib/generate-key.cpp @@ -455,7 +455,7 @@ keygen_primary_merge_defaults(rnp_keygen_primary_desc_t &desc) desc.cert.userid = uid; } -#if defined(ENABLE_CRYPTO_REFRESH) && defined(ENABLE_PQC) +#if defined(ENABLE_PQC) switch (desc.crypto.key_alg) { case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; diff --git a/src/librepgp/stream-common.cpp b/src/librepgp/stream-common.cpp index dc8221f7ce..9c04c6c286 100644 --- a/src/librepgp/stream-common.cpp +++ b/src/librepgp/stream-common.cpp @@ -1189,7 +1189,7 @@ dst_write_src(pgp_source_t *src, pgp_dest_t *dst, uint64_t limit) return dst->werr; } -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) bool have_pkesk_checksum(pgp_pubkey_alg_t alg) { diff --git a/src/librepgp/stream-common.h b/src/librepgp/stream-common.h index 51d7621587..cb3cd167a0 100644 --- a/src/librepgp/stream-common.h +++ b/src/librepgp/stream-common.h @@ -556,7 +556,7 @@ class MemoryDest : public Dest { }; } // namespace rnp -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) bool have_pkesk_checksum(pgp_pubkey_alg_t alg); bool do_encrypt_pkesk_v3_alg_id(pgp_pubkey_alg_t alg); #endif diff --git a/src/librepgp/stream-dump.cpp b/src/librepgp/stream-dump.cpp index 9c27a3f0ef..b2b35d95a4 100644 --- a/src/librepgp/stream-dump.cpp +++ b/src/librepgp/stream-dump.cpp @@ -342,7 +342,7 @@ dst_print_mpi(pgp_dest_t *dst, const char *name, const pgp::mpi &mpi, bool dumpb } } -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) static void dst_print_vec(pgp_dest_t * dst, const char * name, diff --git a/src/librepgp/stream-key.cpp b/src/librepgp/stream-key.cpp index c9c310d853..e9fcda6d40 100644 --- a/src/librepgp/stream-key.cpp +++ b/src/librepgp/stream-key.cpp @@ -957,7 +957,7 @@ pgp_key_pkt_t::parse(pgp_source_t &src) return RNP_ERROR_BAD_FORMAT; } -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) std::vector tmpbuf; #endif diff --git a/src/librepgp/stream-parse.cpp b/src/librepgp/stream-parse.cpp index 1d6c32f21c..bd40c17a62 100644 --- a/src/librepgp/stream-parse.cpp +++ b/src/librepgp/stream-parse.cpp @@ -1567,7 +1567,7 @@ encrypted_start_aead(pgp_source_encrypted_param_t *param, pgp_symm_alg_t alg, ui #endif } -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) /* The crypto refresh mandates that for a X25519/X448 PKESKv3, AES MUST be used. The same is true for the PQC algorithms */ @@ -1587,9 +1587,7 @@ do_enforce_aes_v3pkesk(pgp_pubkey_alg_t alg) case PGP_PKA_KYBER1024_BP384: FALLTHROUGH_STATEMENT; #endif -#if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_X25519: -#endif return true; default: return false; @@ -1632,7 +1630,7 @@ encrypted_try_key(pgp_source_encrypted_param_t *param, } #endif -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) /* check that AES is used when mandated by the standard */ if (do_enforce_aes_v3pkesk(sesskey.alg) && sesskey.version == PGP_PKSK_V3) { switch (sesskey.salg) { @@ -1664,7 +1662,7 @@ encrypted_try_key(pgp_source_encrypted_param_t *param, uint8_t *decbuf_sesskey = decbuf.data(); size_t decbuf_sesskey_len = declen; size_t keylen; -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) if (do_encrypt_pkesk_v3_alg_id(sesskey.alg)) #endif { @@ -1680,7 +1678,7 @@ encrypted_try_key(pgp_source_encrypted_param_t *param, return false; } -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) size_t alg_id_bytes = do_encrypt_pkesk_v3_alg_id(sesskey.alg) ? 1 : 0; size_t checksum_bytes = have_pkesk_checksum(sesskey.alg) ? 2 : 0; #else @@ -1714,7 +1712,7 @@ encrypted_try_key(pgp_source_encrypted_param_t *param, std::vector(decbuf_sesskey, decbuf_sesskey + keylen)); #endif -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) if (have_pkesk_checksum(sesskey.alg)) #endif { @@ -1731,7 +1729,7 @@ encrypted_try_key(pgp_source_encrypted_param_t *param, } } -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) if (sesskey.version == PGP_PKSK_V3) #endif { diff --git a/src/librepgp/stream-write.cpp b/src/librepgp/stream-write.cpp index 7233d67281..1248bce749 100644 --- a/src/librepgp/stream-write.cpp +++ b/src/librepgp/stream-write.cpp @@ -593,7 +593,7 @@ encrypted_add_recipient(pgp_write_handler_t *handler, return RNP_ERROR_NO_SUITABLE_KEY; } -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) /* Crypto Refresh: For X25519/X448 PKESKv3, AES is mandated */ /* PQC: AES is mandated for PKESKv3 */ if (!do_encrypt_pkesk_v3_alg_id(userkey->alg()) && pkesk_version == PGP_PKSK_V3) { @@ -629,7 +629,7 @@ encrypted_add_recipient(pgp_write_handler_t *handler, pkey.salg = param->ctx->ealg; -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) if (pkey.version == PGP_PKSK_V3) { size_t key_offset; if (do_encrypt_pkesk_v3_alg_id(pkey.alg)) { @@ -650,11 +650,11 @@ encrypted_add_recipient(pgp_write_handler_t *handler, } else { // PGP_PKSK_V6 memcpy(&enckey[0], key, keylen); #endif -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) } #endif -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) if (have_pkesk_checksum(pkey.alg)) #endif { diff --git a/src/tests/data/test_v6_valid_data/transferable_pubkey_v6.asc b/src/tests/data/RFC9580/A.3.transferable-pubkey-v6.asc similarity index 100% rename from src/tests/data/test_v6_valid_data/transferable_pubkey_v6.asc rename to src/tests/data/RFC9580/A.3.transferable-pubkey-v6.asc diff --git a/src/tests/data/test_v6_valid_data/transferable_seckey_v6.asc b/src/tests/data/RFC9580/A.4.transferable-seckey-v6.asc similarity index 100% rename from src/tests/data/test_v6_valid_data/transferable_seckey_v6.asc rename to src/tests/data/RFC9580/A.4.transferable-seckey-v6.asc diff --git a/src/tests/data/test_v6_valid_data/a6-sample-cleartext-signed-message.asc b/src/tests/data/RFC9580/A.6.-sample-cleartext-signed-message.asc similarity index 100% rename from src/tests/data/test_v6_valid_data/a6-sample-cleartext-signed-message.asc rename to src/tests/data/RFC9580/A.6.-sample-cleartext-signed-message.asc diff --git a/src/tests/data/test_v6_valid_data/a7-sample-inline-signed-message.asc b/src/tests/data/RFC9580/A.7.-sample-inline-signed-message.asc similarity index 100% rename from src/tests/data/test_v6_valid_data/a7-sample-inline-signed-message.asc rename to src/tests/data/RFC9580/A.7.-sample-inline-signed-message.asc diff --git a/src/tests/data/test_v6_valid_data/v6pkesk.asc b/src/tests/data/RFC9580/A.8.5-v6pkesk-v2seipd similarity index 100% rename from src/tests/data/test_v6_valid_data/v6pkesk.asc rename to src/tests/data/RFC9580/A.8.5-v6pkesk-v2seipd diff --git a/src/tests/exdsa_ecdhkem.cpp b/src/tests/exdsa_ecdhkem.cpp index 83b770e45c..0e6356984f 100644 --- a/src/tests/exdsa_ecdhkem.cpp +++ b/src/tests/exdsa_ecdhkem.cpp @@ -24,7 +24,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) +#if defined(ENABLE_CRYPTO_REFRESH) #include "rnp_tests.h" #include "crypto/exdsa_ecdhkem.h" diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp index 1ac7940375..bde15290f8 100644 --- a/src/tests/ffi-enc.cpp +++ b/src/tests/ffi-enc.cpp @@ -899,9 +899,9 @@ TEST_F(rnp_tests, test_ffi_decrypt_v6_pkesk_test_vector) rnp_output_t output = NULL; assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_true(import_all_keys(ffi, "data/test_v6_valid_data/transferable_seckey_v6.asc")); + assert_true(import_all_keys(ffi, "data/RFC9580/A.4.transferable-seckey-v6.asc")); - assert_rnp_success(rnp_input_from_path(&input, "data/test_v6_valid_data/v6pkesk.asc")); + assert_rnp_success(rnp_input_from_path(&input, "data/RFC9580/A.8.5-v6pkesk-v2seipd")); assert_non_null(input); assert_rnp_success(rnp_output_to_null(&output)); assert_rnp_success(rnp_decrypt(ffi, input, output)); @@ -920,10 +920,10 @@ TEST_F(rnp_tests, test_ffi_verify_v2_seipd_test_vector) rnp_op_verify_t verify = NULL; assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_true(import_all_keys(ffi, "data/test_v6_valid_data/transferable_seckey_v6.asc")); + assert_true(import_all_keys(ffi, "data/RFC9580/A.4.transferable-seckey-v6.asc")); assert_rnp_success(rnp_input_from_path( - &input, "data/test_v6_valid_data/a7-sample-inline-signed-message.asc")); + &input, "data/RFC9580/A.7.-sample-inline-signed-message.asc")); assert_non_null(input); assert_rnp_success(rnp_output_to_path(&output, "decrypted")); assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); @@ -953,10 +953,10 @@ TEST_F(rnp_tests, test_ffi_verify_v2_seipd_cleartext_test_vector) rnp_op_verify_t verify = NULL; assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_true(import_all_keys(ffi, "data/test_v6_valid_data/transferable_seckey_v6.asc")); + assert_true(import_all_keys(ffi, "data/RFC9580/A.4.transferable-seckey-v6.asc")); assert_rnp_success(rnp_input_from_path( - &input, "data/test_v6_valid_data/a6-sample-cleartext-signed-message.asc")); + &input, "data/RFC9580/A.6.-sample-cleartext-signed-message.asc")); assert_non_null(input); assert_rnp_success(rnp_output_to_path(&output, "decrypted")); assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); @@ -1086,7 +1086,7 @@ TEST_F(rnp_tests, test_ffi_encrypt_pk_with_v6_key) // setup FFI assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_true(import_all_keys(ffi, "data/test_v6_valid_data/transferable_seckey_v6.asc")); + assert_true(import_all_keys(ffi, "data/RFC9580/A.4.transferable-seckey-v6.asc")); // No other cipher can be used here: the only 128-bit block ciphers in OpenPGP aside from // AES are Camellia and TwoFish. diff --git a/src/tests/ffi-key.cpp b/src/tests/ffi-key.cpp index 5090c23b1b..04dd84f534 100644 --- a/src/tests/ffi-key.cpp +++ b/src/tests/ffi-key.cpp @@ -3212,7 +3212,7 @@ TEST_F(rnp_tests, test_ffi_v6_cert_import) assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); assert_rnp_success( - rnp_input_from_path(&input, "data/test_v6_valid_data/transferable_pubkey_v6.asc")); + rnp_input_from_path(&input, "data/RFC9580/A.3.transferable-pubkey-v6.asc")); assert_rnp_success( rnp_import_keys(ffi, input, @@ -3326,7 +3326,7 @@ TEST_F(rnp_tests, test_ffi_v6_seckey_import) assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); assert_rnp_success( - rnp_input_from_path(&input, "data/test_v6_valid_data/transferable_seckey_v6.asc")); + rnp_input_from_path(&input, "data/RFC9580/A.4.transferable-seckey-v6.asc")); assert_rnp_success( rnp_import_keys(ffi, input, From 746a6bb95cffb88cd6bd22649147f672ad1ee6a7 Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Fri, 1 Nov 2024 08:42:15 +0100 Subject: [PATCH 12/15] update PQC test vectors to -06 draft --- src/lib/crypto/kmac.cpp | 2 +- .../v6-eddsa-sample-message | 1 - .../v6-eddsa-sample-message.asc | 62 +-- .../v6-eddsa-sample-pk.asc | 74 ++-- .../v6-eddsa-sample-sk.asc | 82 ++-- .../v6-mldsa-65-sample-message.asc | 103 +++++ .../v6-mldsa-65-sample-pk.asc | 288 +++++++++++++ .../v6-mldsa-65-sample-sk.asc | 291 +++++++++++++ .../v6-mldsa-87-sample-message.asc | 143 +++++++ .../v6-mldsa-87-sample-pk.asc | 397 +++++++++++++++++ .../v6-mldsa-87-sample-sk.asc | 401 ++++++++++++++++++ .../v6-mldsa-sample-message.asc | 103 ----- .../v6-mldsa-sample-pk.asc | 289 ------------- .../v6-mldsa-sample-sk.asc | 292 ------------- src/tests/ffi-enc.cpp | 58 ++- src/tests/ffi-key.cpp | 103 ++--- 16 files changed, 1800 insertions(+), 889 deletions(-) delete mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-message.asc create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-pk.asc create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-sk.asc create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-message.asc create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-pk.asc create mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-sk.asc delete mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-message.asc delete mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-pk.asc delete mode 100644 src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc diff --git a/src/lib/crypto/kmac.cpp b/src/lib/crypto/kmac.cpp index 3474de4e6e..1cbe48d162 100644 --- a/src/lib/crypto/kmac.cpp +++ b/src/lib/crypto/kmac.cpp @@ -68,7 +68,7 @@ KMAC256::Input_X(const std::vector &ecc_ciphertext, std::vector res; #if defined(ENABLE_PQC_DBG_LOG) - RNP_LOG_NO_POS_INFO("KMAC256 Key_K: "); + RNP_LOG_NO_POS_INFO("KMAC256 Input_X: "); RNP_LOG_U8VEC(" - eccCipherText: %s", ecc_ciphertext); RNP_LOG_U8VEC(" - mlkemCipherText: %s", kyber_ciphertext); RNP_LOG_U8VEC(" - ecdhPublicKey: %s", ecc_pub); diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message deleted file mode 100644 index 73709ba686..0000000000 --- a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message +++ /dev/null @@ -1 +0,0 @@ -Testing diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message.asc index 075ba32528..1deaa152ab 100644 --- a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message.asc +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message.asc @@ -1,34 +1,34 @@ -----BEGIN PGP MESSAGE----- -wcPtBiEGVrSmanmpRfWJ0fSGnhAPXuAkNJhxdH1utflntzaDWSJpKMYMwW51QMqU -ybrp5IxkE11EchQ+4CJX4GR82u38j1TkkMTI0Q+AWKlxREu4kujxt/1OiaeIfvZy -+sd5N07Ee86U1boyzCj5ypd5l1W61BE1d9iOc1VTfbVUDy6c21KO6Pki2Ls8R6gH -zGK2FT2F3RHyHIsF0ae5Ctg52E82moqzj9KCKghrgQe/2rNDzRDH4hc0G+rh6sbu -tb0eDnDIp0fvx/6Zroj9AQuUonJYAKLKD4RCFaO9+eXsqhIGVNLNdsBm5cDhyy65 -TKrG5FLaCbnDLoCzn6zvw9JrYwnnyN+XCQd4cMU4rs9bTdFti6f1gxksqkm3ChVs -fjsT5QspDB6RBALSA0+O101ONuh+r0Cssl5rZvSf1f8B/n4j4tds4hUlaCREoGpn -igJpo0TYPb0b37AgElVf9BmqCxo4SceoT1Go1QgyUL+1WPsCueCzzoMXxA02niAD -rFHkIbg/9600HD5yiAAsFGPMH/8rdmSCamtOKQoQmPQY7MJOOqOjkxPZWb0waAL8 -dCe6D/yt1z07EVxbF2kAirRCYu396JJ3U0vTilxPi/7OoETCp2wUkKuxLAce9ul1 -LYEPY+XN+faacpl9xwBLLTBZA8OV7vD3MChPcTwZsrlQBA0UALbVyzwMBlzh086M -OmezZ36KaiTSEXn5zPxFt9b4q3HBks655hAwJ2+rAV9rJy4trXWEDlz86oOf7MP8 -gXxmbEetvDDSdnEpnxR5GwsqljnO8UhTVXXFsp0LNCORmwA+n1t8UjQssj9uwO2T -9Y9UJOKQosuSwDvCR64zOGTPwn1w6FSZK34hzeOYYaFTsZjP5QtkrIvvlXdkp/6f -2bL9S4dEaSjWZMoR0NSKtvoY6Vjj342tPlwUhS28uP8w5/MZJgX/vJHSZAoi7vCy -fMOklznFInSs85vADxGVxGuaAVZcz8KlGXkTH0EfhKRhOaRrZALID6jF363cwcB/ -i1YH56Fc5f8wixPwTu9ntZ36q/FMisQZKbJxA3YQO4XCSBzunYUqOFdtg3fJntnH -dHx6nQS0JXCkjDc7gd6Yr7NbcRWUidE/oHSBBpBwiRQju8M8cXaeHMzFczUQjPx/ -k8Xtr7gwEcVdGBdSbS0RBwVy5eiIGYVUAVTg8773bdhXvD4yTVRuPGbYm474MtlE -bgkUch8PxInr8+muA1AcKg3uqwWbcpX/Q56RHIYNbU22Vcl3Nq6UwKqqHaeKjdL4 -aHauPmHOWxgK+lHvZS2Lhg8T1Su0qsO0xOIeZpfOEAr+aNrjpGr7Bj5eOJOBjJQT -1jEHhgIK37QaplKTBf3kc/TH7w1AIpVuJPzi4IXGRy6uwvdfQuOAeYv1c5LnOsKH -dmTZgsg6tSOV+3eSKoQmnTecOoEddtVfQsXRx+QGxsbvSM2B5qyCSo8fFgbeCajs -yRdjjPV7A+exaF/WgAszi+nD/Zka0xIE3g1nCCSRn27NAtrM4jaNHlKg4DZNAC9u -3dsfp/lAeSjDHjkLzOQep10o7Gg+1qFvNwGjOvHCX+LyVEcGIlH1dF+JjfZobWMr -0sBWAgkCDDhxVuabuQ83wJb46Gor24w4/x4ugBmr7KrjzM14lyjnB8uDnTpkfpoB -L5vCzO7FQfnbUWha456roBnRAOUhcqGhdqTPChnrt/ie/PUSfWZlZZh9aS+U50eK -WGIJER2n2A1WfEnYfy155ipf3z1D+ritS9p7hzlVOQpb/xdVHnga9gfrpWljyX0L -YRIL7wh5YjHL940kwgDtA9ZWZ8R3PLPkgOE7Jw/xUTz+QXqRK4R9SubGttmoQy7Y -liWLjUnl5sbm/rsSqmAHdOdz4WYdwWO5eJoJ3/rH0uGZQEHQq6U/iYidTHp+OS8h -Ww4/1zLtOw89HhwLpSN0vk87TV3ZgYVTZlVFwOOEKasNf9VhWIvFS48= +wcPtBiEGlSHzTnMKAF/F+RKwiNpxD/LzQMgs46VSaDO/16AFAAZp12rf5d71LZmG +LEW1pZBMiqisCsiktUVXXM+PYJl8DWSQ7ME6vkg+P3ore3b+JIgBSfHF1GXc5QmP +PgseDd1UkIh/UnH6ZgcFoB8qYM4U/dcyJW6KaJ+Xthe3xHLqSQJsWX3W8DQZg9cx +hJGw0w4KhJDI1XMOdkxvoNHXxjOUY2u6grshRv+ZotIo3/tHYx9chNaEzDJYEuUA +bimXa0wZLmhs6XLSDZlQZZkoZWu9VW8Bj+fAxdV4eiEBLmG/wbx2o9bM0Dqhgjwo +uX0Z6neIfafyMD/IsXaoIXQ4P1yV2eBRrOt+53pcoPfmyyvL9XKURaIKYDWn+qxT +ml24/RIoUJS4tuAL1gV1CanLbly7fwClYf8lSHXZKUs8gT8/kFBgHm6/GEhMUZsW +AbFc8bBid6Xhn064F0qc2tLd2dhebidQpzcj9HfBznKI/BVR/Xezib0YWKfBdgl5 +TM2HyT4wskVZkLTMrCpOxQiabab1xrwLoKwRMuIKDhSK2+FCR6lURM7++v0LRnbp +Eao//OMFjf28ZE2WUYSw4v+YD4XsTXL09Pbeq4l727sCXyLwM+TIhg9gLTSw5oJp +3VYkhiOBCH9XE5YxUfbTOaXBPko7TZFG24r17SAB+VJJodbwCZcwEKDY4lePQaU/ +VOgxLYjg5rJ47GSFD28Gzy0j9uxRF0oQXdr4qhiiAx4MR95QJmoCfdjJrlR6Wz0R +BHyBOliT8WdX+2Y+LusA59D8sTwtoUpiyYzvnpLEbuDAUwpf2xPU43RvdUX50FW+ +0A056CVb03a5LfgYY+ZVJZJQk9xo5iiYATplyj3qcgmXKTubDQff5hANf2phHXtn +YM1rOsvpFV8j0BkUarV69S5t3WF9FnMrJnvEFr0uB6YbwE4et+yv6uSddHgyP1Mm +2/YIiSIfVDDDP4hp1Mrkml9yO3LSAqHR38lmfI/ntfk4zJJecaUR1VNwe/Ug/I/l +/lVEVWXjPFxIkgSsGbx9QhlTwznjg0qDPPav/PoAHMIKhrMurptng5GnM2+Gr4hy +gmG4GGej7wiBjL+wDnsUTKpoXI+yuPheSEBeSvoU8d5+DDTkP9SNHIUU6QP5RnMi +UCOeEdY8vC8u/CaSNPsTssyJHfhkI/BGM69AS/utgsJDFRihPmQWlLAHDoxsfkHe +MDbEk+bA/ZfoSMn0LyE3NX3VYWLkks55SiiJfjtT7xEjLm9DEJOSb4MyKoKuEwh9 +pwTbbxXygWTBtL94qPP+WjnnkxwfXmSppvJ4dURetvTcNy8qHmUZa8j/DayTHvGt +ikImucay37Y+9391NfQRU2tDWq3HNI8CgjlHnKtx0vs45WdxGCH0wwyRTxXAHsfp +3T7v518Dl3glS8rGKfQGCxj/wi31QV4M2EZWPiLrcCWPkz2ccGy0EeYh69EgcQT9 +SZltG/KubODTgKI/CZU6BllqBWVjsr4AdQljRQmKE2EFs/US/JwKol8J5f/TH2qw +5wUAYMO8IyiAToM17rIwyMlFjgbeLuvuD8vcd5aX+zxuJB8V1m3D+1jbsqkSmzUq +0sBWAgkCDHfs49bZvfQupKHCdO7zxX61s2AmsHg+m1xLwNq8HJJ5nYTg4yWFjgdh +ViFNMOfei8vwq6PlVhLsvqQN5GSsceFqxzmjR5vKuJyB8ZRQL533Y1iJ2nKBt6dt +5IZXMm2NLZXM0vsCbb9fybphgMAZSItwHbGqptqqXa1dnlCgmTZ7wce5My2qRT0a +wUpp9w+toLPdgATpsYSVzzXKDCZz5nbhFaJO4dgAP+MNT8e16H1nAt2M+SWh9m0+ +XG3mdXCpDNUINEvDx/amIQvIq9bo39IjLMIl9HU9Q7aEKXWgVL89JKDmOk+etbNd +7EdJjpSgQmN6qFtZ/C8krfENn0qu0xo8FlXp4Oc7rsGAbmSH0lMJIW8= -----END PGP MESSAGE----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-pk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-pk.asc index d1989d7547..0737483b6f 100644 --- a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-pk.asc +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-pk.asc @@ -1,41 +1,41 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -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 -----END PGP PUBLIC KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc index cf84b5ab2a..a848807ee6 100644 --- a/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc @@ -1,44 +1,44 @@ -----BEGIN PGP PRIVATE KEY BLOCK----- -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 -BoWQifC9dbHD5JNv0/6CMXFZagQABA== +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 +nsXEzDINi5gbRcp0g8r/VikcaTR4Cg== -----END PGP PRIVATE KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-message.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-message.asc new file mode 100644 index 0000000000..146b360e70 --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-message.asc @@ -0,0 +1,103 @@ +-----BEGIN PGP MESSAGE----- + +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 +-----END PGP MESSAGE----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-pk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-pk.asc new file mode 100644 index 0000000000..401ee1a3f1 --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-pk.asc @@ -0,0 +1,288 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +AAAAAAAAAAAABQwRGCAn +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-sk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-sk.asc new file mode 100644 index 0000000000..8cd68130ab --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-sk.asc @@ -0,0 +1,291 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +-----END PGP PRIVATE KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-message.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-message.asc new file mode 100644 index 0000000000..116bb2ba46 --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-message.asc @@ -0,0 +1,143 @@ +-----BEGIN PGP MESSAGE----- + +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 +blJ0gqdnPik2He+PKxP6lKHLdfYz3Mws +-----END PGP MESSAGE----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-pk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-pk.asc new file mode 100644 index 0000000000..caa047381e --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-pk.asc @@ -0,0 +1,397 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +BxATHCUsM0M= +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-sk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-sk.asc new file mode 100644 index 0000000000..8901973a00 --- /dev/null +++ b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-sk.asc @@ -0,0 +1,401 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +WVtldYiOnLnO2f8AAAAAAAAAAAcQExwlLDND +-----END PGP PRIVATE KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-message.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-message.asc deleted file mode 100644 index ebeff16a59..0000000000 --- a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-message.asc +++ /dev/null @@ -1,103 +0,0 @@ ------BEGIN PGP MESSAGE----- - -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 ------END PGP MESSAGE----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-pk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-pk.asc deleted file mode 100644 index ef527fd69b..0000000000 --- a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-pk.asc +++ /dev/null @@ -1,289 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -R7PWnOYif6JqzzgsxWnC1nmVEAeWtw17s4tuke4cRh5owUNJBZZygKBJsn37H5cX -oZHw8uzmESNxgMxw9zVUoeOc+l4eX5ynM+FxmXhxIhOH1sQluFb1MpQglVPBrWzz -/2NKvV9tsQq+lISpoewliGawL6Vh7X8qyvzad1LmofEp0p9RrUwl/iGveCR0bdX1 -1D5P/JyetN97OmQoLGmI9FWWrCZ46rlqMuH6+53iejXDUGXOnn0D27y3MdmFtklI -MOMNapEt5Bz7iFbtA0uxPBD6TjlYwP5l6FEzjVyE/KeOyv+oVZzTFsKW2mfyfPrj -2N+DyPrMknZzI2joNGUkKfcuRcO681OU6tX3bj01sTWo7F6wg4ZQdT6Aje2ryScj -24eHAjbGRqI7VQGvDx/kYmSXdrbhHG4Hnmk6mhG5NO1kR6Wu8EioJ2wCjfxBmzHG -u4RP2+pWji8KIVeIy3LYfyKpgP8jxx62VbGYrUV4Iag+OPBukfe3QLhr3p5iMnoP -I0ihwNYmCzCvJ76IKLmfjwchvT/W0a29syQuKcNaofjSihIlUs3ug1T/ZG3Xyl7N -F2h0YFHnF7Rpm3WRSZQsePbMGmwiEck0UFP/Z/DPFe1V8lkH3hEty4NtMUqtn5F1 -VHalFs4C5UYKHtBRuZvqjvr0xnbZDFouCc7m4VjplzsS1tSjLl6EAyonAbbQ3vCt -Tn3v9k8Ro8J1p9mRHA1NdBCTI9J8Lkml+f1IIn8gVdHTTiYcqDxM/zRPmC5ve6fA -D+BFh7qvDkIGqpB0FIkHnstjp2/XFaxJ1Xso7e0QYo30tzVyBFNu8KQF8Okeh2Mz -6M2oKn8GInjeDMWD9DxlZwHOnlcWuXFeurj79G5YiUffg9el18KCwIMrnDD48xUq -v1gYDRy/1LAp3AE3nnm5cIPq1Rp4Nsi8DQBOW/ZLJ7HW1IXs6o2U7Hytlx+KLZ2a -3ETlZ5Nu86OwZYhErsLxDd3wF/85NYUzMxvO5uAVO2LO54jKjpTkQIHpMC4n4laH -M3bQJf2HqJIOwEdL44M5vcXV8jgfoKgoVqUlJDP9SBUQZ8aoLWCq9fAaDgjCjh38 -62zrcfclfNlAIFnje1bWz7S+Wr6IkG1vkj7ViGLix2+40wftuzo1+igIutUyRdHN -7I+ggoFBf+zXEbSlMhV6sW5/4PFw//n0+julq35FKj5psG8/XmLZun2E0CMOTNDJ -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 -AAAAAAAAAAAACgwOExkk ------END PGP PUBLIC KEY BLOCK----- diff --git a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc b/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc deleted file mode 100644 index 577ea31841..0000000000 --- a/src/tests/data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc +++ /dev/null @@ -1,292 +0,0 @@ ------BEGIN PGP PRIVATE KEY BLOCK----- - -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 -QkebRJCZxZG/RCVh66PXOpv3JKQY2VscDzf7x22FDxuCDiKR72Mi15XydCi6fnQI -ug1/j6olqArvt51WpBoM/aN5uD7fuzqcv5OaCXuhyck6lhLRfp0/YRaYdlciihOb -1jHtdO4545qpeb/qETayE7R88zrUQLkiNsjdGdxHgs+MHl64KFC3D6mDvkqnFdqb -7Nd4FxDCBGv5ktQlKFKhGWtrR7PWnOYif6JqzzgsxWnC1nmVEAeWtw17s4tuke4c -Rh5owUNJBZZygKBJsn37H5cXoZHw8uzmESNxgMxw9zVUoeOc+l4eX5ynM+FxmXhx -IhOH1sQluFb1MpQglVPBrWzz/2NKvV9tsQq+lISpoewliGawL6Vh7X8qyvzad1Lm -ofEp0p9RrUwl/iGveCR0bdX11D5P/JyetN97OmQoLGmI9FWWrCZ46rlqMuH6+53i -ejXDUGXOnn0D27y3MdmFtklIMOMNapEt5Bz7iFbtA0uxPBD6TjlYwP5l6FEzjVyE -/KeOyv+oVZzTFsKW2mfyfPrj2N+DyPrMknZzI2joNGUkKfcuRcO681OU6tX3bj01 -sTWo7F6wg4ZQdT6Aje2ryScj24eHAjbGRqI7VQGvDx/kYmSXdrbhHG4Hnmk6mhG5 -NO1kR6Wu8EioJ2wCjfxBmzHGu4RP2+pWji8KIVeIy3LYfyKpgP8jxx62VbGYrUV4 -Iag+OPBukfe3QLhr3p5iMnoPI0ihwNYmCzCvJ76IKLmfjwchvT/W0a29syQuKcNa -ofjSihIlUs3ug1T/ZG3Xyl7NF2h0YFHnF7Rpm3WRSZQsePbMGmwiEck0UFP/Z/DP -Fe1V8lkH3hEty4NtMUqtn5F1VHalFs4C5UYKHtBRuZvqjvr0xnbZDFouCc7m4Vjp -lzsS1tSjLl6EAyonAbbQ3vCtTn3v9k8Ro8J1p9mRHA1NdBCTI9J8Lkml+f1IIn8g -VdHTTiYcqDxM/zRPmC5ve6fAD+BFh7qvDkIGqpB0FIkHnstjp2/XFaxJ1Xso7e0Q -Yo30tzVyBFNu8KQF8Okeh2Mz6M2oKn8GInjeDMWD9DxlZwHOnlcWuXFeurj79G5Y -iUffg9el18KCwIMrnDD48xUqv1gYDRy/1LAp3AE3nnm5cIPq1Rp4Nsi8DQBOW/ZL -J7HW1IXs6o2U7Hytlx+KLZ2a3ETlZ5Nu86OwZYhErsLxDd3wF/85NYUzMxvO5uAV -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 ------END PGP PRIVATE KEY BLOCK----- diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp index bde15290f8..1afaeef778 100644 --- a/src/tests/ffi-enc.cpp +++ b/src/tests/ffi-enc.cpp @@ -922,8 +922,8 @@ TEST_F(rnp_tests, test_ffi_verify_v2_seipd_test_vector) assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); assert_true(import_all_keys(ffi, "data/RFC9580/A.4.transferable-seckey-v6.asc")); - assert_rnp_success(rnp_input_from_path( - &input, "data/RFC9580/A.7.-sample-inline-signed-message.asc")); + assert_rnp_success( + rnp_input_from_path(&input, "data/RFC9580/A.7.-sample-inline-signed-message.asc")); assert_non_null(input); assert_rnp_success(rnp_output_to_path(&output, "decrypted")); assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); @@ -955,8 +955,8 @@ TEST_F(rnp_tests, test_ffi_verify_v2_seipd_cleartext_test_vector) assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); assert_true(import_all_keys(ffi, "data/RFC9580/A.4.transferable-seckey-v6.asc")); - assert_rnp_success(rnp_input_from_path( - &input, "data/RFC9580/A.6.-sample-cleartext-signed-message.asc")); + assert_rnp_success( + rnp_input_from_path(&input, "data/RFC9580/A.6.-sample-cleartext-signed-message.asc")); assert_non_null(input); assert_rnp_success(rnp_output_to_path(&output, "decrypted")); assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); @@ -978,35 +978,31 @@ TEST_F(rnp_tests, test_ffi_verify_v2_seipd_cleartext_test_vector) #if defined(ENABLE_PQC) TEST_F(rnp_tests, test_ffi_decrypt_pqc_pkesk_test_vector) { - rnp_ffi_t ffi = NULL; - rnp_input_t input = NULL; - rnp_output_t output = NULL; - - assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_true(import_all_keys(ffi, "data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc")); - assert_true(import_all_keys(ffi, "data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc")); - - assert_rnp_success(rnp_output_to_path(&output, "decrypted")); - assert_rnp_success( - rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message.asc")); - assert_non_null(input); - assert_rnp_success(rnp_decrypt(ffi, input, output)); - assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); - assert_int_equal(unlink("decrypted"), 0); - rnp_input_destroy(input); - rnp_output_destroy(output); + std::vector> key_msg_pairs = { + {"data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc", + "data/draft-ietf-openpgp-pqc/v6-eddsa-sample-message.asc"}, + {"data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-sk.asc", + "data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-message.asc"}, + {"data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-sk.asc", + "data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-message.asc"}}; + + for (auto key_msg_pair : key_msg_pairs) { + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_rnp_success(rnp_output_to_path(&output, "decrypted")); - assert_rnp_success( - rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-mldsa-sample-message.asc")); - assert_non_null(input); - assert_rnp_success(rnp_decrypt(ffi, input, output)); - assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); - assert_int_equal(unlink("decrypted"), 0); - rnp_input_destroy(input); - rnp_output_destroy(output); + assert_true(import_all_keys(ffi, key_msg_pair.first.c_str())); - rnp_ffi_destroy(ffi); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success(rnp_input_from_path(&input, key_msg_pair.second.c_str())); + assert_non_null(input); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + assert_string_equal(file_to_str("decrypted").c_str(), "Testing\n"); + assert_int_equal(unlink("decrypted"), 0); + rnp_input_destroy(input); + rnp_output_destroy(output); + } } TEST_F(rnp_tests, test_ffi_pqc_default_enc_subkey) diff --git a/src/tests/ffi-key.cpp b/src/tests/ffi-key.cpp index 04dd84f534..b59c231eaa 100644 --- a/src/tests/ffi-key.cpp +++ b/src/tests/ffi-key.cpp @@ -3252,69 +3252,46 @@ TEST_F(rnp_tests, test_ffi_v6_cert_import) #if defined(ENABLE_PQC) TEST_F(rnp_tests, test_ffi_pqc_certs) { - rnp_ffi_t ffi = NULL; - rnp_input_t input = NULL; - size_t keycount = 255; - - /* Public Key 1 */ - assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_rnp_success( - rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-eddsa-sample-pk.asc")); - assert_rnp_success( - rnp_import_keys(ffi, - input, - RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SINGLE | RNP_LOAD_SAVE_BASE64, - NULL)); - rnp_input_destroy(input); - assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); - assert_int_equal(keycount, 2); - assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); - assert_int_equal(keycount, 0); - rnp_ffi_destroy(ffi); - - /* Private Key 1 */ - assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_rnp_success( - rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc")); - assert_rnp_success( - rnp_import_keys(ffi, - input, - RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_SINGLE | RNP_LOAD_SAVE_BASE64, - NULL)); - rnp_input_destroy(input); - assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); - assert_int_equal(keycount, 2); - rnp_ffi_destroy(ffi); - - /* Public Key 2 */ - assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_rnp_success( - rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-mldsa-sample-pk.asc")); - assert_rnp_success( - rnp_import_keys(ffi, - input, - RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SINGLE | RNP_LOAD_SAVE_BASE64, - NULL)); - rnp_input_destroy(input); - assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); - assert_int_equal(keycount, 2); - assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); - assert_int_equal(keycount, 0); - rnp_ffi_destroy(ffi); - - /* Private Key 2 */ - assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_rnp_success( - rnp_input_from_path(&input, "data/draft-ietf-openpgp-pqc/v6-mldsa-sample-sk.asc")); - assert_rnp_success( - rnp_import_keys(ffi, - input, - RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_SINGLE | RNP_LOAD_SAVE_BASE64, - NULL)); - rnp_input_destroy(input); - assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); - assert_int_equal(keycount, 2); - rnp_ffi_destroy(ffi); + std::vector> pub_sec_keys = { + {"data/draft-ietf-openpgp-pqc/v6-eddsa-sample-pk.asc", + "data/draft-ietf-openpgp-pqc/v6-eddsa-sample-sk.asc"}, + {"data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-pk.asc", + "data/draft-ietf-openpgp-pqc/v6-mldsa-65-sample-sk.asc"}, + {"data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-pk.asc", + "data/draft-ietf-openpgp-pqc/v6-mldsa-87-sample-sk.asc"}}; + + for (auto pub_sec_key : pub_sec_keys) { + /* Public */ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + size_t keycount = 255; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_input_from_path(&input, pub_sec_key.first.c_str())); + assert_rnp_success(rnp_import_keys(ffi, + input, + RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SINGLE | + RNP_LOAD_SAVE_BASE64, + NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 2); + assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); + assert_int_equal(keycount, 0); + rnp_ffi_destroy(ffi); + + /* Private */ + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_input_from_path(&input, pub_sec_key.second.c_str())); + assert_rnp_success(rnp_import_keys(ffi, + input, + RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_SINGLE | + RNP_LOAD_SAVE_BASE64, + NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); + assert_int_equal(keycount, 2); + rnp_ffi_destroy(ffi); + } } #endif From 49e01590d040772080ccba583c77d6b2b9495243 Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Fri, 1 Nov 2024 11:42:14 +0100 Subject: [PATCH 13/15] fix pqc signature hash binding --- src/lib/crypto/dilithium.cpp | 44 +++++++++++++++++++++++++++------- src/lib/crypto/dilithium.h | 4 ++-- src/lib/crypto/sphincsplus.cpp | 12 ++-------- src/lib/crypto/sphincsplus.h | 2 -- src/lib/generate-key.cpp | 2 +- src/lib/key_material.cpp | 10 ++++++-- src/lib/key_material.hpp | 1 + src/rnpkeys/tui.cpp | 6 ++--- 8 files changed, 53 insertions(+), 28 deletions(-) diff --git a/src/lib/crypto/dilithium.cpp b/src/lib/crypto/dilithium.cpp index 1561b24ef6..acb44b85fa 100644 --- a/src/lib/crypto/dilithium.cpp +++ b/src/lib/crypto/dilithium.cpp @@ -25,6 +25,8 @@ */ #include "dilithium.h" +#include "logging.h" +#include "types.h" #include namespace { @@ -119,19 +121,45 @@ pgp_dilithium_private_key_t::is_valid(rnp::RNG *rng) const } bool -dilithium_hash_allowed(pgp_hash_alg_t hash_alg) +dilithium_hash_allowed(pgp_pubkey_alg_t pk_alg, pgp_hash_alg_t hash_alg) { - switch (hash_alg) { - case PGP_HASH_SHA3_256: - case PGP_HASH_SHA3_512: - return true; + switch (pk_alg) { + case PGP_PKA_DILITHIUM3_ED25519: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM3_P256: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM3_BP256: + return hash_alg == PGP_HASH_SHA3_256; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_P384: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_BP384: + return hash_alg == PGP_HASH_SHA3_512; default: - return false; + RNP_LOG("invalid algorithm ID given"); + throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } } pgp_hash_alg_t -dilithium_default_hash_alg() +dilithium_default_hash_alg(pgp_pubkey_alg_t pk_alg) { - return PGP_HASH_SHA3_256; + switch (pk_alg) { + case PGP_PKA_DILITHIUM3_ED25519: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM3_P256: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM3_BP256: + return PGP_HASH_SHA3_256; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_P384: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_BP384: + return PGP_HASH_SHA3_512; + default: + RNP_LOG("invalid algorithm ID given"); + throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); + } } \ No newline at end of file diff --git a/src/lib/crypto/dilithium.h b/src/lib/crypto/dilithium.h index 18035ce0c1..43ddec016e 100644 --- a/src/lib/crypto/dilithium.h +++ b/src/lib/crypto/dilithium.h @@ -109,8 +109,8 @@ class pgp_dilithium_public_key_t { std::pair dilithium_generate_keypair( rnp::RNG *rng, dilithium_parameter_e dilithium_param); -bool dilithium_hash_allowed(pgp_hash_alg_t hash_alg); +bool dilithium_hash_allowed(pgp_pubkey_alg_t pk_alg, pgp_hash_alg_t hash_alg); -pgp_hash_alg_t dilithium_default_hash_alg(); +pgp_hash_alg_t dilithium_default_hash_alg(pgp_pubkey_alg_t pk_alg); #endif diff --git a/src/lib/crypto/sphincsplus.cpp b/src/lib/crypto/sphincsplus.cpp index 3f673ff849..bd24b6f41f 100644 --- a/src/lib/crypto/sphincsplus.cpp +++ b/src/lib/crypto/sphincsplus.cpp @@ -148,14 +148,6 @@ pgp_sphincsplus_generate(rnp::RNG *rng, pgp_sphincsplus_key_t *material, pgp_pub return RNP_SUCCESS; } -bool -pgp_sphincsplus_public_key_t::validate_signature_hash_requirements( - pgp_hash_alg_t hash_alg) const -{ - /* check if key is allowed with the hash algorithm */ - return sphincsplus_hash_allowed(pk_alg_, hash_alg); -} - bool pgp_sphincsplus_public_key_t::is_valid(rnp::RNG *rng) const { @@ -244,7 +236,7 @@ sphincsplus_hash_allowed(pgp_pubkey_alg_t pk_alg, pgp_hash_alg_t hash_alg) return hash_alg == PGP_HASH_SHA3_512; default: RNP_LOG("invalid algorithm ID given"); - throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); + throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } } @@ -260,6 +252,6 @@ sphincsplus_default_hash_alg(pgp_pubkey_alg_t alg) return PGP_HASH_SHA3_512; default: RNP_LOG("invalid algorithm ID given"); - throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS); + throw rnp::rnp_exception(RNP_ERROR_BAD_STATE); } } \ No newline at end of file diff --git a/src/lib/crypto/sphincsplus.h b/src/lib/crypto/sphincsplus.h index 81bbfad996..614662fbcc 100644 --- a/src/lib/crypto/sphincsplus.h +++ b/src/lib/crypto/sphincsplus.h @@ -105,8 +105,6 @@ class pgp_sphincsplus_public_key_t { bool is_valid(rnp::RNG *rng) const; - bool validate_signature_hash_requirements(pgp_hash_alg_t hash_alg) const; - pgp_pubkey_alg_t alg() const { diff --git a/src/lib/generate-key.cpp b/src/lib/generate-key.cpp index 95379ac4f0..4ea88cef3a 100644 --- a/src/lib/generate-key.cpp +++ b/src/lib/generate-key.cpp @@ -232,7 +232,7 @@ pgp_check_key_hash_requirements(const rnp_keygen_crypto_params_t &crypto) case PGP_PKA_DILITHIUM3_BP256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: - if (!dilithium_hash_allowed(crypto.hash_alg)) { + if (!dilithium_hash_allowed(crypto.key_alg, crypto.hash_alg)) { return false; } break; diff --git a/src/lib/key_material.cpp b/src/lib/key_material.cpp index e68396dfec..d8957a290d 100644 --- a/src/lib/key_material.cpp +++ b/src/lib/key_material.cpp @@ -2131,7 +2131,13 @@ DilithiumEccKeyMaterial::sign(rnp::SecurityContext & ctx, pgp_hash_alg_t DilithiumEccKeyMaterial::adjust_hash(pgp_hash_alg_t hash) const { - return dilithium_default_hash_alg(); + return dilithium_default_hash_alg(alg()); +} + +bool +DilithiumEccKeyMaterial::sig_hash_allowed(pgp_hash_alg_t hash) const +{ + return dilithium_hash_allowed(alg(), hash); } size_t @@ -2266,7 +2272,7 @@ SlhdsaKeyMaterial::adjust_hash(pgp_hash_alg_t hash) const bool SlhdsaKeyMaterial::sig_hash_allowed(pgp_hash_alg_t hash) const { - return key_.pub.validate_signature_hash_requirements(hash); + return sphincsplus_hash_allowed(alg(), hash); } size_t diff --git a/src/lib/key_material.hpp b/src/lib/key_material.hpp index 4f61149b27..39356a43a3 100644 --- a/src/lib/key_material.hpp +++ b/src/lib/key_material.hpp @@ -658,6 +658,7 @@ class DilithiumEccKeyMaterial : public KeyMaterial { pgp_signature_material_t & sig, const rnp::secure_vector &hash) const override; pgp_hash_alg_t adjust_hash(pgp_hash_alg_t hash) const override; + bool sig_hash_allowed(pgp_hash_alg_t hash) const override; size_t bits() const noexcept override; const pgp_dilithium_exdsa_composite_public_key_t & pub() const noexcept; diff --git a/src/rnpkeys/tui.cpp b/src/rnpkeys/tui.cpp index 1b24f1b9f8..52d68bbddb 100644 --- a/src/rnpkeys/tui.cpp +++ b/src/rnpkeys/tui.cpp @@ -348,7 +348,7 @@ rnpkeys_ask_generate_params(rnp_cfg &cfg, FILE *input_fp) break; case 26: cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_DILITHIUM5_ED448); - cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_256); + cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_512); cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER1024_X448); cfg.set_str(CFG_KG_V6_KEY, "true"); break; @@ -360,7 +360,7 @@ rnpkeys_ask_generate_params(rnp_cfg &cfg, FILE *input_fp) break; case 28: cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_DILITHIUM5_P384); - cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_256); + cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_512); cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER1024_P384); cfg.set_str(CFG_KG_V6_KEY, "true"); break; @@ -372,7 +372,7 @@ rnpkeys_ask_generate_params(rnp_cfg &cfg, FILE *input_fp) break; case 30: cfg.set_str(CFG_KG_PRIMARY_ALG, RNP_ALGNAME_DILITHIUM5_BP384); - cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_256); + cfg.set_str(CFG_KG_HASH, RNP_ALGNAME_SHA3_512); cfg.set_str(CFG_KG_SUBKEY_ALG, RNP_ALGNAME_KYBER1024_BP384); cfg.set_str(CFG_KG_V6_KEY, "true"); break; From a584014eb16ab7e8390159eb818716fda3441c17 Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Mon, 4 Nov 2024 16:07:41 +0100 Subject: [PATCH 14/15] sensible values in ec_curves for Ed448/X448 --- src/lib/crypto/ec_curves.cpp | 49 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/lib/crypto/ec_curves.cpp b/src/lib/crypto/ec_curves.cpp index e9e00b97fa..cf040b1a4f 100644 --- a/src/lib/crypto/ec_curves.cpp +++ b/src/lib/crypto/ec_curves.cpp @@ -263,36 +263,35 @@ static const ec_curve_desc_t ec_curves[] = { "0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", "0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", }, -/* TODO: correct values */ #if defined(ENABLE_CRYPTO_REFRESH) {PGP_CURVE_ED448, - 0, - {}, - 0, - "Ed25519", - "Ed25519", + 255, + {0x2B, 0x65, 0x71}, + 3, + "Ed448", + "Ed448", true, - "0x00", - "0x00", - "0x00", - "0x00", - "0x00", - "0x00", - "0x00"}, + "", + "", + "", + "", + "", + "", + ""}, {PGP_CURVE_448, - 0, - {}, - 0x00, - "curve25519", - "Curve25519", + 448, + {0x2B, 0x65, 0x6F}, + 3, + "X448", + "X448", true, - "0x00", - "0x00", - "0x00", - "0x00", - "0x00", - "0x00", - "0x00"}, + "", + "", + "", + "", + "", + "", + ""}, #endif }; From 9b25a0cc0ac5dceecb3ea611e0491cb88c001e51 Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Tue, 12 Nov 2024 10:24:28 +0100 Subject: [PATCH 15/15] fix some stuff after rebase --- src/lib/crypto/hash_common.cpp | 1 + src/lib/generate-key.cpp | 6 ++- src/lib/key_material.cpp | 15 ++++-- src/lib/key_material.hpp | 2 +- src/lib/keygen.cpp | 93 +++++++++++++++++++++++----------- src/lib/pgp-key.cpp | 12 +++-- src/librepgp/stream-dump.cpp | 5 ++ src/librepgp/stream-parse.cpp | 2 + src/tests/ffi-enc.cpp | 3 +- 9 files changed, 100 insertions(+), 39 deletions(-) diff --git a/src/lib/crypto/hash_common.cpp b/src/lib/crypto/hash_common.cpp index 4ea5f67876..44ffc1ae96 100644 --- a/src/lib/crypto/hash_common.cpp +++ b/src/lib/crypto/hash_common.cpp @@ -38,6 +38,7 @@ #include "hash_crc24.hpp" #endif #include +#include static const struct hash_alg_map_t { pgp_hash_alg_t type; diff --git a/src/lib/generate-key.cpp b/src/lib/generate-key.cpp index 4ea88cef3a..e4c0f9dce2 100644 --- a/src/lib/generate-key.cpp +++ b/src/lib/generate-key.cpp @@ -178,6 +178,8 @@ adjust_hash_alg(rnp_keygen_crypto_params_t &crypto) case PGP_PKA_SPHINCSPLUS_SHAKE_128s: crypto.hash_alg = PGP_HASH_SHA3_256; break; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -461,9 +463,11 @@ keygen_primary_merge_defaults(rnp_keygen_primary_desc_t &desc) FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM3_BP256: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: FALLTHROUGH_STATEMENT; - case PGP_PKA_DILITHIUM3_BP256: + case PGP_PKA_DILITHIUM5_Ed448: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: FALLTHROUGH_STATEMENT; diff --git a/src/lib/key_material.cpp b/src/lib/key_material.cpp index d8957a290d..81ab1b9fc5 100644 --- a/src/lib/key_material.cpp +++ b/src/lib/key_material.cpp @@ -152,6 +152,10 @@ KeyParams::create(pgp_pubkey_alg_t alg) return std::unique_ptr(new ECCKeyParams(PGP_CURVE_ED25519)); case PGP_PKA_X25519: return std::unique_ptr(new ECCKeyParams(PGP_CURVE_25519)); + case PGP_PKA_ED448: + return std::unique_ptr(new ECCKeyParams(PGP_CURVE_ED448)); + case PGP_PKA_X448: + return std::unique_ptr(new ECCKeyParams(PGP_CURVE_448)); #endif case PGP_PKA_DSA: return std::unique_ptr(new DSAKeyParams()); @@ -161,7 +165,8 @@ KeyParams::create(pgp_pubkey_alg_t alg) #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: FALLTHROUGH_STATEMENT; - // TODO add case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; + case PGP_PKA_KYBER1024_X448: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P384: @@ -172,7 +177,8 @@ KeyParams::create(pgp_pubkey_alg_t alg) return std::unique_ptr(new MlkemEcdhKeyParams(alg)); case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -409,6 +415,10 @@ KeyMaterial::create(pgp_pubkey_alg_t alg) return std::unique_ptr(new Ed25519KeyMaterial()); case PGP_PKA_X25519: return std::unique_ptr(new X25519KeyMaterial()); + case PGP_PKA_ED448: + return std::unique_ptr(new Ed448KeyMaterial()); + case PGP_PKA_X448: + return std::unique_ptr(new X448KeyMaterial()); #endif case PGP_PKA_SM2: return std::unique_ptr(new SM2KeyMaterial()); @@ -2239,7 +2249,6 @@ SlhdsaKeyMaterial::write_secret(pgp_packet_body_t &pkt) const bool SlhdsaKeyMaterial::generate(rnp::SecurityContext &ctx, const KeyParams ¶ms) { - auto &slhdsa = dynamic_cast(params); if (pgp_sphincsplus_generate(&ctx.rng, &key_, alg_)) { RNP_LOG("failed to generate SLH-DSA key for PK alg %d", alg_); return false; diff --git a/src/lib/key_material.hpp b/src/lib/key_material.hpp index 39356a43a3..0d1438fc28 100644 --- a/src/lib/key_material.hpp +++ b/src/lib/key_material.hpp @@ -550,7 +550,7 @@ class Ed448KeyMaterial : public KeyMaterial { bool parse_secret(pgp_packet_body_t &pkt) noexcept override; void write(pgp_packet_body_t &pkt) const override; void write_secret(pgp_packet_body_t &pkt) const override; - bool generate(rnp::SecurityContext &ctx, const KeyParams ¶ms) override; + bool generate(rnp::SecurityContext &ctx, const KeyParams ¶ms) override; rnp_result_t verify(const rnp::SecurityContext & ctx, const pgp_signature_material_t & sig, const rnp::secure_vector &hash) const override; diff --git a/src/lib/keygen.cpp b/src/lib/keygen.cpp index 3e132560b9..298ab0bf8d 100644 --- a/src/lib/keygen.cpp +++ b/src/lib/keygen.cpp @@ -42,6 +42,39 @@ KeygenParams::check_defaults() noexcept if (hash_ == PGP_HASH_UNKNOWN) { hash_ = alg_ == PGP_PKA_SM2 ? PGP_HASH_SM3 : DEFAULT_PGP_HASH_ALG; } +#if defined(ENABLE_PQC) + // ensure PQC key hash binding + switch (alg_) { + case PGP_PKA_DILITHIUM3_ED25519: + FALLTHROUGH_STATEMENT; + 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 (!dilithium_hash_allowed(alg_, hash_)) { + hash_ = dilithium_default_hash_alg(alg_); + } + break; + + case PGP_PKA_SPHINCSPLUS_SHAKE_128f: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_128s: + FALLTHROUGH_STATEMENT; + case PGP_PKA_SPHINCSPLUS_SHAKE_256s: + if (!sphincsplus_hash_allowed(alg_, hash_)) { + hash_ = sphincsplus_default_hash_alg(alg_); + } + break; + default: + break; + } +#endif + pgp_hash_alg_t min_hash = key_params_->min_hash(); if (Hash::size(hash_) < Hash::size(min_hash)) { hash_ = min_hash; @@ -66,7 +99,8 @@ KeygenParams::validate() const noexcept break; case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; - // TODO: add case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_ED448: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P384: @@ -74,7 +108,7 @@ KeygenParams::validate() const noexcept case PGP_PKA_DILITHIUM3_BP256: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: - if (!dilithium_hash_allowed(hash())) { + if (!dilithium_hash_allowed(alg(), hash())) { RNP_LOG("invalid hash algorithm for the dilithium key"); return false; } @@ -132,37 +166,38 @@ KeygenParams::validate(const BindingParams &binding) const noexcept return validate(); } -static const id_str_pair pubkey_alg_map[] = {{PGP_PKA_RSA, "RSA (Encrypt or Sign)"}, - {PGP_PKA_RSA_ENCRYPT_ONLY, "RSA Encrypt-Only"}, - {PGP_PKA_RSA_SIGN_ONLY, "RSA Sign-Only"}, - {PGP_PKA_ELGAMAL, "Elgamal (Encrypt-Only)"}, - {PGP_PKA_DSA, "DSA"}, - {PGP_PKA_ECDH, "ECDH"}, - {PGP_PKA_ECDSA, "ECDSA"}, - {PGP_PKA_EDDSA, "EdDSA"}, - {PGP_PKA_SM2, "SM2"}, +static const id_str_pair pubkey_alg_map[] = { + {PGP_PKA_RSA, "RSA (Encrypt or Sign)"}, + {PGP_PKA_RSA_ENCRYPT_ONLY, "RSA Encrypt-Only"}, + {PGP_PKA_RSA_SIGN_ONLY, "RSA Sign-Only"}, + {PGP_PKA_ELGAMAL, "Elgamal (Encrypt-Only)"}, + {PGP_PKA_DSA, "DSA"}, + {PGP_PKA_ECDH, "ECDH"}, + {PGP_PKA_ECDSA, "ECDSA"}, + {PGP_PKA_EDDSA, "EdDSA"}, + {PGP_PKA_SM2, "SM2"}, #if defined(ENABLE_CRYPTO_REFRESH) - {PGP_PKA_ED25519, "ED25519"}, - {PGP_PKA_X25519, "X25519"}, + {PGP_PKA_ED25519, "ED25519"}, + {PGP_PKA_X25519, "X25519"}, #endif #if defined(ENABLE_PQC) - {PGP_PKA_KYBER768_X25519, "ML-KEM-768_X25519"}, - //{PGP_PKA_KYBER1024_X448, "Kyber-X448"}, - {PGP_PKA_KYBER768_P256, "ML-KEM-768_P256"}, - {PGP_PKA_KYBER1024_P384, "ML-KEM-1024_P384"}, - {PGP_PKA_KYBER768_BP256, "ML-KEM-768_BP256"}, - {PGP_PKA_KYBER1024_BP384, "ML-KEM-1024_BP384"}, - {PGP_PKA_DILITHIUM3_ED25519, "ML-DSA-65_ED25519"}, - //{PGP_PKA_DILITHIUM5_ED448, "Dilithium-ED448"}, - {PGP_PKA_DILITHIUM3_P256, "ML-DSA-65_P256"}, - {PGP_PKA_DILITHIUM5_P384, "ML-DSA-87_P384"}, - {PGP_PKA_DILITHIUM3_BP256, "ML-DSA-65_BP256"}, - {PGP_PKA_DILITHIUM5_BP384, "ML-DSA-87_BP384"}, - {PGP_PKA_SPHINCSPLUS_SHAKE_128f, "SLH-DSA-SHAKE-128f"}, - {PGP_PKA_SPHINCSPLUS_SHAKE_128s, "SLH-DSA-SHAKE-128s"}, - {PGP_PKA_SPHINCSPLUS_SHAKE_256s, "SLH-DSA-SHAKE-256s"}, + {PGP_PKA_KYBER768_X25519, "ML-KEM-768_X25519"}, + //{PGP_PKA_KYBER1024_X448, "Kyber-X448"}, + {PGP_PKA_KYBER768_P256, "ML-KEM-768_P256"}, + {PGP_PKA_KYBER1024_P384, "ML-KEM-1024_P384"}, + {PGP_PKA_KYBER768_BP256, "ML-KEM-768_BP256"}, + {PGP_PKA_KYBER1024_BP384, "ML-KEM-1024_BP384"}, + {PGP_PKA_DILITHIUM3_ED25519, "ML-DSA-65_ED25519"}, + //{PGP_PKA_DILITHIUM5_ED448, "Dilithium-ED448"}, + {PGP_PKA_DILITHIUM3_P256, "ML-DSA-65_P256"}, + {PGP_PKA_DILITHIUM5_P384, "ML-DSA-87_P384"}, + {PGP_PKA_DILITHIUM3_BP256, "ML-DSA-65_BP256"}, + {PGP_PKA_DILITHIUM5_BP384, "ML-DSA-87_BP384"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_128f, "SLH-DSA-SHAKE-128f"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_128s, "SLH-DSA-SHAKE-128s"}, + {PGP_PKA_SPHINCSPLUS_SHAKE_256s, "SLH-DSA-SHAKE-256s"}, #endif - {0, NULL}}; + {0, NULL}}; bool KeygenParams::generate(pgp_key_pkt_t &seckey, bool primary) diff --git a/src/lib/pgp-key.cpp b/src/lib/pgp-key.cpp index 51e2b0da07..3326165e8a 100644 --- a/src/lib/pgp-key.cpp +++ b/src/lib/pgp-key.cpp @@ -1242,20 +1242,24 @@ pgp_key_t::is_pqc_alg() const 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_X448: + FALLTHROUGH_STATEMENT; + case PGP_PKA_KYBER1024_P384: + FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_ED25519: 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_ED448: + FALLTHROUGH_STATEMENT; + case PGP_PKA_DILITHIUM5_P384: + FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_SPHINCSPLUS_SHAKE_128f: diff --git a/src/librepgp/stream-dump.cpp b/src/librepgp/stream-dump.cpp index b2b35d95a4..5b1e2bedf9 100644 --- a/src/librepgp/stream-dump.cpp +++ b/src/librepgp/stream-dump.cpp @@ -1246,6 +1246,11 @@ stream_dump_pk_session_key(rnp_dump_ctx_t *ctx, pgp_source_t *src, pgp_dest_t *d dst_print_vec( dst, "x25519 encrypted session key", material.x25519.enc_sess_key, ctx->dump_mpi); break; + case PGP_PKA_X448: + dst_print_vec(dst, "x448 ephemeral public key", material.x448.eph_key, ctx->dump_mpi); + dst_print_vec( + dst, "x448 encrypted session key", material.x448.enc_sess_key, ctx->dump_mpi); + break; #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: diff --git a/src/librepgp/stream-parse.cpp b/src/librepgp/stream-parse.cpp index bd40c17a62..b0665042bf 100644 --- a/src/librepgp/stream-parse.cpp +++ b/src/librepgp/stream-parse.cpp @@ -1589,6 +1589,8 @@ do_enforce_aes_v3pkesk(pgp_pubkey_alg_t alg) #endif case PGP_PKA_X25519: return true; + case PGP_PKA_X448: + return true; default: return false; } diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp index 1afaeef778..135cfd3565 100644 --- a/src/tests/ffi-enc.cpp +++ b/src/tests/ffi-enc.cpp @@ -836,12 +836,13 @@ TEST_F(rnp_tests, test_ffi_pqc_gen_enc_sign) std::vector> primary_sub = { {"ML-DSA-65+ED25519", "ML-KEM-768+X25519"}, {"ML-DSA-65+ECDSA-P256", "ML-KEM-768+ECDH-P256"}, + {"ML-DSA-87+ED448", "ML-KEM-1024+X448"}, {"ML-DSA-87+ECDSA-P384", "ML-KEM-1024+ECDH-P384"}, {"ML-DSA-65+ECDSA-BP256", "ML-KEM-768+ECDH-BP256"}, {"ML-DSA-87+ECDSA-BP384", "ML-KEM-1024+ECDH-BP384"}, {"SLH-DSA-SHAKE-128f", "ML-KEM-768+X25519"}, {"SLH-DSA-SHAKE-128s", "ML-KEM-768+X25519"}, - {"SLH-DSA-SHAKE-256s", "ML-KEM-768+X25519"}}; + {"SLH-DSA-SHAKE-256s", "ML-KEM-1024+X448"}}; for (auto pk_algs : primary_sub) { rnp_ffi_t ffi = NULL; rnp_key_handle_t key = NULL;