Skip to content

Commit

Permalink
address review comments; add flag for preferring pqc encryption subkey
Browse files Browse the repository at this point in the history
  • Loading branch information
TJ-91 committed Sep 24, 2024
1 parent 701c882 commit 8e59c0c
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 19 deletions.
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_CRYPTO_REFRESH)
#define RNP_KEY_PREFER_PQC_ENC_SUBKEY (1U << 1)
#endif

/**
* User id type
Expand Down Expand Up @@ -1928,6 +1931,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 @@ -3194,6 +3200,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
28 changes: 17 additions & 11 deletions src/lib/pgp-key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,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,16 +476,18 @@ find_suitable_key(pgp_op_t op, pgp_key_t *key, rnp::KeyProvider *key_provider, b
continue;
}
#if defined(ENABLE_PQC)
/* prefer PQC over non-PQC. 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;
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())) {
Expand Down
3 changes: 2 additions & 1 deletion src/lib/pgp-key.h
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,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 @@ -2576,8 +2576,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 @@ -2600,6 +2608,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 @@ -6743,6 +6765,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 @@ -6768,8 +6795,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_CRYPTO_REFRESH)
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
1 change: 0 additions & 1 deletion src/tests/cli_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,6 @@ def rnp_encrypt_and_sign_file(src, dst, recipients, encrpswd, signers, signpswd,
if ret != 0:
raise_err('rnp encrypt-and-sign failed', err)


def rnp_decrypt_file(src, dst, password = PASSWORD):
pipe = pswd_pipe(password)
ret, out, err = run_proc(
Expand Down
65 changes: 65 additions & 0 deletions src/tests/ffi-enc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,71 @@ TEST_F(rnp_tests, test_ffi_decrypt_pqc_pkesk_test_vector)

rnp_ffi_destroy(ffi);
}

TEST_F(rnp_tests, test_ffi_pqc_default_enc_subkey)
{
rnp_ffi_t ffi = NULL;
rnp_key_handle_t key1 = NULL;
rnp_key_handle_t key2 = NULL;
rnp_key_handle_t defkey1 = NULL;
rnp_key_handle_t defkey2 = NULL;
rnp_op_generate_t op = NULL;
assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));

/* generate key 1 */
assert_rnp_success(rnp_op_generate_create(&op, ffi, "ML-DSA-65+ED25519"));
assert_rnp_success(rnp_op_generate_set_hash(op, "SHA3-256"));
assert_rnp_success(rnp_op_generate_execute(op));

assert_rnp_success(rnp_op_generate_get_key(op, &key1));
assert_non_null(key1);
assert_rnp_success(rnp_op_generate_destroy(op));
op = NULL;
assert_rnp_success(rnp_op_generate_subkey_create(&op, ffi, key1, "ML-KEM-768+X25519"));
assert_rnp_success(rnp_op_generate_execute(op));
rnp_op_generate_destroy(op);
op = NULL;
assert_rnp_success(rnp_op_generate_subkey_create(&op, ffi, key1, "ECDH"));
assert_rnp_success(rnp_op_generate_set_curve(op, "NIST P-256"));
assert_rnp_success(rnp_op_generate_execute(op));
rnp_op_generate_destroy(op);
op = NULL;

/* generate key 2 */
assert_rnp_success(rnp_op_generate_create(&op, ffi, "ML-DSA-65+ED25519"));
assert_rnp_success(rnp_op_generate_set_hash(op, "SHA3-256"));
assert_rnp_success(rnp_op_generate_execute(op));
assert_rnp_success(rnp_op_generate_get_key(op, &key2));
assert_non_null(key2);
assert_rnp_success(rnp_op_generate_destroy(op));
op = NULL;
assert_rnp_success(rnp_op_generate_subkey_create(&op, ffi, key2, "ECDH"));
assert_rnp_success(rnp_op_generate_set_curve(op, "NIST P-256"));
assert_rnp_success(rnp_op_generate_execute(op));
rnp_op_generate_destroy(op);
op = NULL;
assert_rnp_success(rnp_op_generate_subkey_create(&op, ffi, key2, "ML-KEM-768+X25519"));
assert_rnp_success(rnp_op_generate_execute(op));
rnp_op_generate_destroy(op);
op = NULL;

/* check default key */
assert_rnp_success(
rnp_key_get_default_key(key1, "encrypt", RNP_KEY_PREFER_PQC_ENC_SUBKEY, &defkey1));
// PQC key is older but preferred
assert(defkey1->pub->alg() == PGP_PKA_KYBER768_X25519);
assert_rnp_success(
rnp_key_get_default_key(key2, "encrypt", RNP_KEY_PREFER_PQC_ENC_SUBKEY, &defkey2));
// PQC key is newer and preferred
assert(defkey2->pub->alg() == PGP_PKA_KYBER768_X25519);

/* cleanup */
rnp_key_handle_destroy(key1);
rnp_key_handle_destroy(key2);
rnp_key_handle_destroy(defkey1);
rnp_key_handle_destroy(defkey2);
rnp_ffi_destroy(ffi);
}
#endif

TEST_F(rnp_tests, test_ffi_encrypt_pk_with_v6_key)
Expand Down

0 comments on commit 8e59c0c

Please sign in to comment.