Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix for ML-KEM PKESK and adding PQC CLI tests #2221

Merged
merged 17 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions include/rnp/rnp.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ typedef uint32_t rnp_result_t;
* Flags for default key selection.
*/
#define RNP_KEY_SUBKEYS_ONLY (1U << 0)
#if defined(RNP_EXPERIMENTAL_PQC)
#define RNP_KEY_PREFER_PQC_ENC_SUBKEY (1U << 1)
#endif

/**
* User id type
Expand Down Expand Up @@ -2349,6 +2352,9 @@ RNP_API rnp_result_t rnp_key_get_subkey_at(rnp_key_handle_t key,
* @param flags possible values: RNP_KEY_SUBKEYS_ONLY - select only subkeys,
* otherwise if flags is 0, primary key can be returned if
* it is suitable for specified usage.
* Note: If RNP_EXPERIMENTAL_PQC is set, then the flag
* RNP_KEY_PREFER_PQC_ENC_SUBKEY can be used to prefer PQC-encryption subkeys
* over non-PQC-encryption subkeys
* @param default_key on success resulting key handle will be stored here, otherwise it
* will contain NULL value. You must free this handle after use with
* rnp_key_handle_destroy().
Expand Down Expand Up @@ -3615,6 +3621,18 @@ RNP_API rnp_result_t rnp_op_encrypt_add_recipient(rnp_op_encrypt_t op, rnp_key_h
RNP_API rnp_result_t rnp_op_encrypt_enable_pkesk_v6(rnp_op_encrypt_t op);
#endif

#if defined(RNP_EXPERIMENTAL_PQC)
/**
* @brief Prefer using PQC subkeys over non-PQC subkeys when encrypting.
* NOTE: This is an experimental feature and this function can be replaced (or removed)
* at any time.
*
* @param op opaque encrypting context. Must be allocated and initialized.
* @return RNP_SUCCESS or errorcode if failed.
*/
RNP_API rnp_result_t rnp_op_encrypt_prefer_pqc_enc_subkey(rnp_op_encrypt_t op);
#endif

/**
* @brief Add signature to encrypting context, so data will be encrypted and signed.
*
Expand Down
56 changes: 55 additions & 1 deletion src/lib/pgp-key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,11 @@ pgp_subkey_set_expiration(pgp_key_t * sub,
}

pgp_key_t *
find_suitable_key(pgp_op_t op, pgp_key_t *key, rnp::KeyProvider *key_provider, bool no_primary)
find_suitable_key(pgp_op_t op,
pgp_key_t * key,
rnp::KeyProvider *key_provider,
bool no_primary,
bool pref_pqc_sub)
{
if (!key || !key_provider) {
return NULL;
Expand Down Expand Up @@ -472,6 +476,21 @@ find_suitable_key(pgp_op_t op, pgp_key_t *key, rnp::KeyProvider *key_provider, b
if (!cur || !cur->usable_for(op)) {
continue;
}
#if defined(ENABLE_PQC)
if (pref_pqc_sub && op == PGP_OP_ENCRYPT) {
/* prefer PQC encryption over non-PQC encryption. Assume non-PQC key is only there
* for backwards compatibility. */
if (subkey && subkey->is_pqc_alg() && !cur->is_pqc_alg()) {
/* do not override already found PQC key with non-PQC key */
continue;
}
if (subkey && cur->is_pqc_alg() && !subkey->is_pqc_alg()) {
/* override non-PQC key with PQC key */
subkey = cur;
continue;
}
}
#endif
if (!subkey || (cur->creation() > subkey->creation())) {
subkey = cur;
}
Expand Down Expand Up @@ -1301,6 +1320,41 @@ pgp_key_t::has_secret() const noexcept
}
}

#if defined(ENABLE_PQC)
bool
pgp_key_t::is_pqc_alg() const
{
switch (alg()) {
case PGP_PKA_KYBER768_X25519:
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:
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_BP384:
FALLTHROUGH_STATEMENT;
case PGP_PKA_SPHINCSPLUS_SHA2:
FALLTHROUGH_STATEMENT;
case PGP_PKA_SPHINCSPLUS_SHAKE:
return true;
default:
return false;
}
}
#endif

bool
pgp_key_t::usable_for(pgp_op_t op, bool if_secret) const
{
Expand Down
6 changes: 5 additions & 1 deletion src/lib/pgp-key.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ struct pgp_key_t {
bool can_certify() const noexcept;
bool can_encrypt() const noexcept;
bool has_secret() const noexcept;
#if defined(ENABLE_PQC)
bool is_pqc_alg() const;
#endif
/**
* @brief Check whether key is usable for the specified operation.
*
Expand Down Expand Up @@ -688,6 +691,7 @@ bool pgp_subkey_set_expiration(pgp_key_t * sub,
pgp_key_t *find_suitable_key(pgp_op_t op,
pgp_key_t * key,
rnp::KeyProvider *key_provider,
bool no_primary = false);
bool no_primary = false,
bool pref_pqc_sub = false);

#endif // RNP_PACKET_KEY_H
35 changes: 31 additions & 4 deletions src/lib/rnp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2594,8 +2594,16 @@ try {
return RNP_ERROR_NULL_POINTER;
}

pgp_key_t *key = find_suitable_key(
PGP_OP_ENCRYPT, get_key_prefer_public(handle), &handle->ffi->key_provider);
#if defined(ENABLE_PQC)
bool prefer_pqc = op->rnpctx.pref_pqc_enc_subkey;
#else
bool prefer_pqc = false;
#endif
pgp_key_t *key = find_suitable_key(PGP_OP_ENCRYPT,
get_key_prefer_public(handle),
&handle->ffi->key_provider,
false,
prefer_pqc);
if (!key) {
return RNP_ERROR_NO_SUITABLE_KEY;
}
Expand All @@ -2618,6 +2626,20 @@ try {
FFI_GUARD
#endif

#if defined(RNP_EXPERIMENTAL_PQC)
rnp_result_t
rnp_op_encrypt_prefer_pqc_enc_subkey(rnp_op_encrypt_t op)
try {
if (!op) {
return RNP_ERROR_NULL_POINTER;
}

op->rnpctx.pref_pqc_enc_subkey = true;
return RNP_SUCCESS;
}
FFI_GUARD
#endif

rnp_result_t
rnp_op_encrypt_add_signature(rnp_op_encrypt_t op,
rnp_key_handle_t key,
Expand Down Expand Up @@ -7296,6 +7318,11 @@ try {
return RNP_ERROR_BAD_PARAMETERS;
}
bool no_primary = extract_flag(flags, RNP_KEY_SUBKEYS_ONLY);
#if defined(ENABLE_PQC)
bool prefer_pqc_enc_subkey = extract_flag(flags, RNP_KEY_PREFER_PQC_ENC_SUBKEY);
#else
bool prefer_pqc_enc_subkey = false;
#endif
if (flags) {
FFI_LOG(primary_key->ffi, "Invalid flags: %" PRIu32, flags);
return RNP_ERROR_BAD_PARAMETERS;
Expand All @@ -7321,8 +7348,8 @@ try {
if (!key) {
return RNP_ERROR_BAD_PARAMETERS;
}
pgp_key_t *defkey =
find_suitable_key(op, key, &primary_key->ffi->key_provider, no_primary);
pgp_key_t *defkey = find_suitable_key(
op, key, &primary_key->ffi->key_provider, no_primary, prefer_pqc_enc_subkey);
if (!defkey) {
*default_key = NULL;
return RNP_ERROR_NO_SUITABLE_KEY;
Expand Down
9 changes: 7 additions & 2 deletions src/librepgp/stream-ctx.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ typedef struct rnp_symmetric_pass_info_t {
* - halg : hash algorithm used during key derivation for password-based encryption
* - ealg, aalg, abits : symmetric encryption algorithm and AEAD parameters if used
* - recipients : list of key ids used to encrypt data to
* - enable_pkesk_v6 : if true and each recipient in the list of recipients has the
* capability, allows PKESKv5/SEIPDv2
* - enable_pkesk_v6 (Only if defined: ENABLE_CRYPTO_REFRESH): if true and each recipient in
* the list of recipients has the capability, allows PKESKv6/SEIPDv2
* - pref_pqc_enc_subkey (Only if defined: ENABLE_PQC): if true, prefers PQC subkey over
* non-PQC subkey for encryption.
* - passwords : list of passwords used for password-based encryption
* - filename, filemtime, zalg, zlevel : see previous
* - pkeskv6_capable() : returns true if all keys support PKESKv6+SEIPDv2, false otherwise
Expand Down Expand Up @@ -108,6 +110,9 @@ typedef struct rnp_ctx_t {
bool no_wrap{}; /* do not wrap source in literal data packet */
#if defined(ENABLE_CRYPTO_REFRESH)
bool enable_pkesk_v6{}; /* allows pkesk v6 if list of recipients is suitable */
#endif
#if defined(ENABLE_PQC)
bool pref_pqc_enc_subkey{}; /* prefer to encrypt to PQC subkey */
#endif
std::list<pgp_key_t *> recipients{}; /* recipients of the encrypted message */
std::list<rnp_symmetric_pass_info_t> passwords{}; /* passwords to encrypt message */
Expand Down
7 changes: 5 additions & 2 deletions src/librepgp/stream-packet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1373,14 +1373,17 @@ pgp_pk_sesskey_t::write_material(const pgp_encrypted_material_t &material)
FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER768_BP256:
FALLTHROUGH_STATEMENT;
case PGP_PKA_KYBER1024_BP384:
case PGP_PKA_KYBER1024_BP384: {
pktbody.add(material.kyber_ecdh.composite_ciphertext);
pktbody.add_byte(static_cast<uint8_t>(material.kyber_ecdh.wrapped_sesskey.size()) + 1);
uint8_t opt_salg_length = (version == PGP_PKSK_V3) ? 1 : 0;
pktbody.add_byte(static_cast<uint8_t>(material.kyber_ecdh.wrapped_sesskey.size()) +
opt_salg_length);
if (version == PGP_PKSK_V3) {
pktbody.add_byte(salg); /* added as plaintext */
}
pktbody.add(material.kyber_ecdh.wrapped_sesskey);
break;
}
#endif
default:
RNP_LOG("Unknown pk alg: %d", (int) alg);
Expand Down
Loading
Loading