Skip to content

Commit 9da228e

Browse files
committed
add v6 SKESK
1 parent 0d5c2b9 commit 9da228e

18 files changed

+409
-26
lines changed

include/repgp/repgp_def.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,13 @@ typedef enum {
481481
PGP_C_UNKNOWN = 255
482482
} pgp_compression_type_t;
483483

484-
enum { PGP_SKSK_V4 = 4, PGP_SKSK_V5 = 5 };
484+
typedef enum {
485+
PGP_SKSK_V4 = 4,
486+
PGP_SKSK_V5 = 5,
487+
#if defined(ENABLE_CRYPTO_REFRESH)
488+
PGP_SKSK_V6 = 6
489+
#endif
490+
} pgp_skesk_version_t;
485491
typedef enum {
486492
PGP_PKSK_V3 = 3,
487493
#if defined(ENABLE_CRYPTO_REFRESH)

include/rnp/rnp.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3691,6 +3691,17 @@ RNP_API rnp_result_t rnp_op_encrypt_add_recipient(rnp_op_encrypt_t op, rnp_key_h
36913691
* @return RNP_SUCCESS or errorcode if failed.
36923692
*/
36933693
RNP_API rnp_result_t rnp_op_encrypt_enable_pkesk_v6(rnp_op_encrypt_t op);
3694+
3695+
/**
3696+
* @brief Enables the creation of SKESK v6 (instead of v4) which results in the use of SEIPDv2.
3697+
* The actually created version depends on whether an AEAD algorithm has been chosen.
3698+
* NOTE: This is an experimental feature and this function can be replaced (or removed)
3699+
* at any time.
3700+
*
3701+
* @param op opaque encrypting context. Must be allocated and initialized.
3702+
* @return RNP_SUCCESS or errorcode if failed.
3703+
*/
3704+
RNP_API rnp_result_t rnp_op_encrypt_enable_skesk_v6(rnp_op_encrypt_t op);
36943705
#endif
36953706

36963707
#if defined(RNP_EXPERIMENTAL_PQC)

src/lib/rnp.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2620,6 +2620,18 @@ try {
26202620
return RNP_SUCCESS;
26212621
}
26222622
FFI_GUARD
2623+
2624+
rnp_result_t
2625+
rnp_op_encrypt_enable_skesk_v6(rnp_op_encrypt_t op)
2626+
try {
2627+
if (!op) {
2628+
return RNP_ERROR_NULL_POINTER;
2629+
}
2630+
2631+
op->rnpctx.enable_skesk_v6 = true;
2632+
return RNP_SUCCESS;
2633+
}
2634+
FFI_GUARD
26232635
#endif
26242636

26252637
#if defined(RNP_EXPERIMENTAL_PQC)

src/librepgp/stream-ctx.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ typedef struct rnp_symmetric_pass_info_t {
7474
* - recipients : list of key ids used to encrypt data to
7575
* - enable_pkesk_v6 (Only if defined: ENABLE_CRYPTO_REFRESH): if true and each recipient in
7676
* the list of recipients has the capability, allows PKESKv6/SEIPDv2
77+
* - enable_skesk_v6 : if true and AEAD cipher is chosen, creates SKESKv6/SEIPDv2
7778
* - pref_pqc_enc_subkey (Only if defined: ENABLE_PQC): if true, prefers PQC subkey over
7879
* non-PQC subkey for encryption.
7980
* - passwords : list of passwords used for password-based encryption
@@ -112,6 +113,7 @@ typedef struct rnp_ctx_t {
112113
bool no_wrap{}; /* do not wrap source in literal data packet */
113114
#if defined(ENABLE_CRYPTO_REFRESH)
114115
bool enable_pkesk_v6{}; /* allows pkesk v6 if list of recipients is suitable */
116+
bool enable_skesk_v6{}; /* allows skesk v6 if chosen cipher is suitable */
115117
#endif
116118
#if defined(ENABLE_PQC)
117119
bool pref_pqc_enc_subkey{}; /* prefer to encrypt to PQC subkey */

src/librepgp/stream-packet.cpp

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -939,11 +939,43 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
939939
pgp_packet_body_t pktbody(PGP_PKT_SK_SESSION_KEY);
940940
/* version and algorithm fields */
941941
pktbody.add_byte(version);
942+
#if defined(ENABLE_CRYPTO_REFRESH)
943+
uint8_t s2k_len;
944+
/* A one-octet scalar octet count for the 5 fields following this octet. */
945+
/* TODO: unify with pgp_key_pkt_t::s2k_specifier_len() */
946+
if (version == PGP_SKSK_V6) {
947+
switch (s2k.specifier) {
948+
case PGP_S2KS_SIMPLE:
949+
s2k_len = 2;
950+
break;
951+
case PGP_S2KS_SALTED:
952+
s2k_len = 10;
953+
break;
954+
case PGP_S2KS_ITERATED_AND_SALTED:
955+
s2k_len = 11;
956+
break;
957+
default:
958+
RNP_LOG("invalid specifier");
959+
throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
960+
}
961+
pktbody.add_byte(3 + s2k_len + ivlen);
962+
}
963+
#endif
942964
pktbody.add_byte(alg);
943-
if (version == PGP_SKSK_V5) {
965+
if (version == PGP_SKSK_V5
966+
#if defined(ENABLE_CRYPTO_REFRESH)
967+
|| version == PGP_SKSK_V6
968+
#endif
969+
) {
944970
pktbody.add_byte(aalg);
945971
}
946-
/* S2K specifier */
972+
/* S2K specifier */
973+
#if defined(ENABLE_CRYPTO_REFRESH)
974+
/* A one-octet scalar octet count of the following field. */
975+
if (version == PGP_SKSK_V6) {
976+
pktbody.add_byte(s2k_len);
977+
}
978+
#endif
947979
pktbody.add_byte(s2k.specifier);
948980
pktbody.add_byte(s2k.hash_alg);
949981

@@ -962,7 +994,11 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
962994
throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
963995
}
964996
/* v5 : iv */
965-
if (version == PGP_SKSK_V5) {
997+
if (version == PGP_SKSK_V5
998+
#if defined(ENABLE_CRYPTO_REFRESH)
999+
|| version == PGP_SKSK_V6
1000+
#endif
1001+
) {
9661002
pktbody.add(iv, ivlen);
9671003
}
9681004
/* encrypted key and auth tag for v5 */
@@ -973,6 +1009,82 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
9731009
pktbody.write(dst);
9741010
}
9751011

1012+
#if defined(ENABLE_CRYPTO_REFRESH)
1013+
rnp_result_t
1014+
pgp_sk_sesskey_t::parse_v6(pgp_packet_body_t &pkt)
1015+
{
1016+
uint8_t bt;
1017+
uint8_t octet_count;
1018+
uint8_t s2k_len;
1019+
1020+
/* A one-octet scalar octet count for the 5 fields following this octet. */
1021+
/* TODO: do we need to check octet_count? */
1022+
if (!pkt.get(octet_count)) {
1023+
RNP_LOG("failed to get octet count of next 5 fields");
1024+
return RNP_ERROR_BAD_FORMAT;
1025+
}
1026+
1027+
/* symmetric algorithm */
1028+
if (!pkt.get(bt)) {
1029+
RNP_LOG("failed to get symm alg");
1030+
return RNP_ERROR_BAD_FORMAT;
1031+
}
1032+
alg = (pgp_symm_alg_t) bt;
1033+
1034+
/* aead algorithm */
1035+
if (!pkt.get(bt)) {
1036+
RNP_LOG("failed to get aead alg");
1037+
return RNP_ERROR_BAD_FORMAT;
1038+
}
1039+
aalg = (pgp_aead_alg_t) bt;
1040+
if ((aalg != PGP_AEAD_EAX) && (aalg != PGP_AEAD_OCB)) {
1041+
RNP_LOG("unsupported AEAD algorithm : %d", (int) aalg);
1042+
return RNP_ERROR_BAD_PARAMETERS;
1043+
}
1044+
1045+
/* A one-octet scalar octet count of the following field. */
1046+
/* TODO: do we need to check s2k_len? */
1047+
if (!pkt.get(s2k_len)) {
1048+
RNP_LOG("failed to get octet count of next 5 fields");
1049+
return RNP_ERROR_BAD_FORMAT;
1050+
}
1051+
1052+
/* s2k */
1053+
if (!pkt.get(s2k)) {
1054+
RNP_LOG("failed to parse s2k");
1055+
return RNP_ERROR_BAD_FORMAT;
1056+
}
1057+
1058+
size_t noncelen = pgp_cipher_aead_nonce_len(aalg);
1059+
size_t taglen = pgp_cipher_aead_tag_len(aalg);
1060+
size_t keylen = 0;
1061+
1062+
if (pkt.left() > noncelen + taglen + PGP_MAX_KEY_SIZE) {
1063+
RNP_LOG("too long esk");
1064+
return RNP_ERROR_BAD_FORMAT;
1065+
}
1066+
if (pkt.left() < noncelen + taglen + 8) {
1067+
RNP_LOG("too short esk");
1068+
return RNP_ERROR_BAD_FORMAT;
1069+
}
1070+
/* iv */
1071+
if (!pkt.get(iv, noncelen)) {
1072+
RNP_LOG("failed to get iv");
1073+
return RNP_ERROR_BAD_FORMAT;
1074+
}
1075+
ivlen = noncelen;
1076+
1077+
/* key */
1078+
keylen = pkt.left();
1079+
if (!pkt.get(enckey, keylen)) {
1080+
RNP_LOG("failed to get key");
1081+
return RNP_ERROR_BAD_FORMAT;
1082+
}
1083+
enckeylen = keylen;
1084+
return RNP_SUCCESS;
1085+
}
1086+
#endif
1087+
9761088
rnp_result_t
9771089
pgp_sk_sesskey_t::parse(pgp_source_t &src)
9781090
{
@@ -985,6 +1097,12 @@ pgp_sk_sesskey_t::parse(pgp_source_t &src)
9851097
/* version */
9861098
uint8_t bt;
9871099
if (!pkt.get(bt) || ((bt != PGP_SKSK_V4) && (bt != PGP_SKSK_V5))) {
1100+
#if defined(ENABLE_CRYPTO_REFRESH)
1101+
if (bt == PGP_SKSK_V6) {
1102+
version = bt;
1103+
return parse_v6(pkt);
1104+
}
1105+
#endif
9881106
RNP_LOG("wrong packet version");
9891107
return RNP_ERROR_BAD_FORMAT;
9901108
}

src/librepgp/stream-packet.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ typedef struct pgp_sk_sesskey_t {
212212

213213
void write(pgp_dest_t &dst) const;
214214
rnp_result_t parse(pgp_source_t &src);
215+
#if defined(ENABLE_CRYPTO_REFRESH)
216+
private:
217+
rnp_result_t parse_v6(pgp_packet_body_t &pkt);
218+
#endif
215219
} pgp_sk_sesskey_t;
216220

217221
/** pgp_one_pass_sig_t */

src/librepgp/stream-parse.cpp

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#ifdef ENABLE_CRYPTO_REFRESH
5151
#include "crypto/hkdf.hpp"
5252
#include "v2_seipd.h"
53+
#include "crypto/hkdf.hpp"
5354
#endif
5455

5556
#ifdef HAVE_ZLIB_H
@@ -1702,7 +1703,11 @@ encrypted_try_password(pgp_source_encrypted_param_t *param, const char *password
17021703
continue;
17031704
}
17041705
keyavail = true;
1705-
} else if (skey.version == PGP_SKSK_V5) {
1706+
} else if (skey.version == PGP_SKSK_V5
1707+
#if defined(ENABLE_CRYPTO_REFRESH)
1708+
|| skey.version == PGP_SKSK_V6
1709+
#endif
1710+
) {
17061711
#if !defined(ENABLE_AEAD)
17071712
continue;
17081713
#else
@@ -1716,6 +1721,31 @@ encrypted_try_password(pgp_source_encrypted_param_t *param, const char *password
17161721
alg = skey.alg;
17171722

17181723
/* initialize cipher */
1724+
#if defined(ENABLE_CRYPTO_REFRESH)
1725+
if (skey.version == PGP_SKSK_V6) {
1726+
/* For v6 SKESK, we use the S2K derived key as input to the KDF */
1727+
auto kdf = rnp::Hkdf::create(PGP_HASH_SHA256);
1728+
1729+
std::vector<uint8_t> kdf_info;
1730+
kdf_info.push_back(PGP_PKT_SK_SESSION_KEY | PGP_PTAG_ALWAYS_SET |
1731+
PGP_PTAG_NEW_FORMAT);
1732+
kdf_info.push_back(skey.version);
1733+
kdf_info.push_back(skey.alg);
1734+
kdf_info.push_back(skey.aalg);
1735+
1736+
std::vector<uint8_t> kdf_input(keybuf.data(),
1737+
keybuf.data() + pgp_key_size(skey.alg));
1738+
1739+
kdf->extract_expand(NULL,
1740+
0, // no salt
1741+
kdf_input.data(),
1742+
kdf_input.size(),
1743+
kdf_info.data(),
1744+
kdf_info.size(),
1745+
keybuf.data(),
1746+
keybuf.size());
1747+
}
1748+
#endif
17191749
if (!pgp_cipher_aead_init(&crypt, skey.alg, skey.aalg, keybuf.data(), true)) {
17201750
continue;
17211751
}
@@ -2163,14 +2193,12 @@ encrypted_read_packet_data(pgp_source_encrypted_param_t *param)
21632193
}
21642194
#ifdef ENABLE_CRYPTO_REFRESH
21652195
else if (SEIPD_version == PGP_SE_IP_DATA_V2) {
2166-
/* SKESK v6 is not yet implemented, thus we must not attempt to decrypt
2167-
SEIPDv2 here
2168-
TODO: Once SKESK v6 is implemented, replace this check with a check for
2169-
consistency between SEIPD and SKESK version
2170-
*/
2171-
if (param->symencs.size() > 0) {
2172-
RNP_LOG("SEIPDv2 not usable with SKESK version");
2173-
return RNP_ERROR_BAD_FORMAT;
2196+
for (auto symenc : param->symencs) {
2197+
// consistency check if SEIPDv2 is only coupled with SKESKv6
2198+
if (symenc.version != PGP_SKSK_V6) {
2199+
RNP_LOG("SEIPDv2 not usable with SKESK version");
2200+
return RNP_ERROR_BAD_FORMAT;
2201+
}
21742202
}
21752203

21762204
param->auth_type = rnp::AuthType::AEADv2;

0 commit comments

Comments
 (0)