Skip to content

Commit 08d586f

Browse files
committed
add v6 SKESK
1 parent fbfb2c8 commit 08d586f

20 files changed

+360
-23
lines changed

include/repgp/repgp_def.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,13 @@ typedef enum {
499499
PGP_C_UNKNOWN = 255
500500
} pgp_compression_type_t;
501501

502-
enum { PGP_SKSK_V4 = 4, PGP_SKSK_V5 = 5 };
502+
typedef enum {
503+
PGP_SKSK_V4 = 4,
504+
PGP_SKSK_V5 = 5,
505+
#if defined(ENABLE_CRYPTO_REFRESH)
506+
PGP_SKSK_V6 = 6
507+
#endif
508+
} pgp_skesk_version_t;
503509
typedef enum {
504510
PGP_PKSK_V3 = 3,
505511
#if defined(ENABLE_CRYPTO_REFRESH)

include/rnp/rnp.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3619,6 +3619,17 @@ RNP_API rnp_result_t rnp_op_encrypt_add_recipient(rnp_op_encrypt_t op, rnp_key_h
36193619
* @return RNP_SUCCESS or errorcode if failed.
36203620
*/
36213621
RNP_API rnp_result_t rnp_op_encrypt_enable_pkesk_v6(rnp_op_encrypt_t op);
3622+
3623+
/**
3624+
* @brief Enables the creation of SKESK v6 (instead of v4) which results in the use of SEIPDv2.
3625+
* The actually created version depends on whether an AEAD algorithm has been chosen.
3626+
* NOTE: This is an experimental feature and this function can be replaced (or removed)
3627+
* at any time.
3628+
*
3629+
* @param op opaque encrypting context. Must be allocated and initialized.
3630+
* @return RNP_SUCCESS or errorcode if failed.
3631+
*/
3632+
RNP_API rnp_result_t rnp_op_encrypt_enable_skesk_v6(rnp_op_encrypt_t op);
36223633
#endif
36233634

36243635
#if defined(RNP_EXPERIMENTAL_PQC)

src/lib/rnp.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2623,6 +2623,18 @@ try {
26232623
return RNP_SUCCESS;
26242624
}
26252625
FFI_GUARD
2626+
2627+
rnp_result_t
2628+
rnp_op_encrypt_enable_skesk_v6(rnp_op_encrypt_t op)
2629+
try {
2630+
if (!op) {
2631+
return RNP_ERROR_NULL_POINTER;
2632+
}
2633+
2634+
op->rnpctx.enable_skesk_v6 = true;
2635+
return RNP_SUCCESS;
2636+
}
2637+
FFI_GUARD
26262638
#endif
26272639

26282640
#if defined(RNP_EXPERIMENTAL_PQC)
@@ -2767,7 +2779,7 @@ try {
27672779
return RNP_ERROR_BAD_PARAMETERS;
27682780
}
27692781
#ifdef ENABLE_CRYPTO_REFRESH
2770-
if (op->rnpctx.aalg == PGP_AEAD_NONE && op->rnpctx.enable_pkesk_v6) {
2782+
if (op->rnpctx.aalg == PGP_AEAD_NONE && (op->rnpctx.enable_pkesk_v6)) {
27712783
FFI_LOG(op->ffi,
27722784
"Setting AEAD algorithm to PGP_AEAD_NONE (%s) would contradict the previously "
27732785
"enabled PKESKv6 setting",

src/librepgp/stream-ctx.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ typedef struct rnp_symmetric_pass_info_t {
7272
* - recipients : list of key ids used to encrypt data to
7373
* - enable_pkesk_v6 (Only if defined: ENABLE_CRYPTO_REFRESH): if true and each recipient in
7474
* the list of recipients has the capability, allows PKESKv6/SEIPDv2
75+
* - enable_skesk_v6 : if true and AEAD cipher is chosen, creates SKESKv6/SEIPDv2
7576
* - pref_pqc_enc_subkey (Only if defined: ENABLE_PQC): if true, prefers PQC subkey over
7677
* non-PQC subkey for encryption.
7778
* - passwords : list of passwords used for password-based encryption
@@ -110,6 +111,7 @@ typedef struct rnp_ctx_t {
110111
bool no_wrap{}; /* do not wrap source in literal data packet */
111112
#if defined(ENABLE_CRYPTO_REFRESH)
112113
bool enable_pkesk_v6{}; /* allows pkesk v6 if list of recipients is suitable */
114+
bool enable_skesk_v6{}; /* allows skesk v6 if chosen cipher is suitable */
113115
#endif
114116
#if defined(ENABLE_PQC)
115117
bool pref_pqc_enc_subkey{}; /* prefer to encrypt to PQC subkey */

src/librepgp/stream-packet.cpp

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -936,11 +936,43 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
936936
pgp_packet_body_t pktbody(PGP_PKT_SK_SESSION_KEY);
937937
/* version and algorithm fields */
938938
pktbody.add_byte(version);
939+
#if defined(ENABLE_CRYPTO_REFRESH)
940+
uint8_t s2k_len;
941+
/* A one-octet scalar octet count for the 5 fields following this octet. */
942+
/* TODO: unify with pgp_key_pkt_t::s2k_specifier_len() */
943+
if (version == PGP_SKSK_V6) {
944+
switch (s2k.specifier) {
945+
case PGP_S2KS_SIMPLE:
946+
s2k_len = 2;
947+
break;
948+
case PGP_S2KS_SALTED:
949+
s2k_len = 10;
950+
break;
951+
case PGP_S2KS_ITERATED_AND_SALTED:
952+
s2k_len = 11;
953+
break;
954+
default:
955+
RNP_LOG("invalid specifier");
956+
throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
957+
}
958+
pktbody.add_byte(3 + s2k_len + ivlen);
959+
}
960+
#endif
939961
pktbody.add_byte(alg);
940-
if (version == PGP_SKSK_V5) {
962+
if (version == PGP_SKSK_V5
963+
#if defined(ENABLE_CRYPTO_REFRESH)
964+
|| version == PGP_SKSK_V6
965+
#endif
966+
) {
941967
pktbody.add_byte(aalg);
942968
}
943-
/* S2K specifier */
969+
/* S2K specifier */
970+
#if defined(ENABLE_CRYPTO_REFRESH)
971+
/* A one-octet scalar octet count of the following field. */
972+
if (version == PGP_SKSK_V6) {
973+
pktbody.add_byte(s2k_len);
974+
}
975+
#endif
944976
pktbody.add_byte(s2k.specifier);
945977
pktbody.add_byte(s2k.hash_alg);
946978

@@ -959,7 +991,11 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
959991
throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
960992
}
961993
/* v5 : iv */
962-
if (version == PGP_SKSK_V5) {
994+
if (version == PGP_SKSK_V5
995+
#if defined(ENABLE_CRYPTO_REFRESH)
996+
|| version == PGP_SKSK_V6
997+
#endif
998+
) {
963999
pktbody.add(iv, ivlen);
9641000
}
9651001
/* encrypted key and auth tag for v5 */
@@ -970,6 +1006,82 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
9701006
pktbody.write(dst);
9711007
}
9721008

1009+
#if defined(ENABLE_CRYPTO_REFRESH)
1010+
rnp_result_t
1011+
pgp_sk_sesskey_t::parse_v6(pgp_packet_body_t &pkt)
1012+
{
1013+
uint8_t bt;
1014+
uint8_t octet_count;
1015+
uint8_t s2k_len;
1016+
1017+
/* A one-octet scalar octet count for the 5 fields following this octet. */
1018+
/* TODO: do we need to check octet_count? */
1019+
if (!pkt.get(octet_count)) {
1020+
RNP_LOG("failed to get octet count of next 5 fields");
1021+
return RNP_ERROR_BAD_FORMAT;
1022+
}
1023+
1024+
/* symmetric algorithm */
1025+
if (!pkt.get(bt)) {
1026+
RNP_LOG("failed to get symm alg");
1027+
return RNP_ERROR_BAD_FORMAT;
1028+
}
1029+
alg = (pgp_symm_alg_t) bt;
1030+
1031+
/* aead algorithm */
1032+
if (!pkt.get(bt)) {
1033+
RNP_LOG("failed to get aead alg");
1034+
return RNP_ERROR_BAD_FORMAT;
1035+
}
1036+
aalg = (pgp_aead_alg_t) bt;
1037+
if ((aalg != PGP_AEAD_EAX) && (aalg != PGP_AEAD_OCB)) {
1038+
RNP_LOG("unsupported AEAD algorithm : %d", (int) aalg);
1039+
return RNP_ERROR_BAD_PARAMETERS;
1040+
}
1041+
1042+
/* A one-octet scalar octet count of the following field. */
1043+
/* TODO: do we need to check s2k_len? */
1044+
if (!pkt.get(s2k_len)) {
1045+
RNP_LOG("failed to get octet count of next 5 fields");
1046+
return RNP_ERROR_BAD_FORMAT;
1047+
}
1048+
1049+
/* s2k */
1050+
if (!pkt.get(s2k)) {
1051+
RNP_LOG("failed to parse s2k");
1052+
return RNP_ERROR_BAD_FORMAT;
1053+
}
1054+
1055+
size_t noncelen = pgp_cipher_aead_nonce_len(aalg);
1056+
size_t taglen = pgp_cipher_aead_tag_len(aalg);
1057+
size_t keylen = 0;
1058+
1059+
if (pkt.left() > noncelen + taglen + PGP_MAX_KEY_SIZE) {
1060+
RNP_LOG("too long esk");
1061+
return RNP_ERROR_BAD_FORMAT;
1062+
}
1063+
if (pkt.left() < noncelen + taglen + 8) {
1064+
RNP_LOG("too short esk");
1065+
return RNP_ERROR_BAD_FORMAT;
1066+
}
1067+
/* iv */
1068+
if (!pkt.get(iv, noncelen)) {
1069+
RNP_LOG("failed to get iv");
1070+
return RNP_ERROR_BAD_FORMAT;
1071+
}
1072+
ivlen = noncelen;
1073+
1074+
/* key */
1075+
keylen = pkt.left();
1076+
if (!pkt.get(enckey, keylen)) {
1077+
RNP_LOG("failed to get key");
1078+
return RNP_ERROR_BAD_FORMAT;
1079+
}
1080+
enckeylen = keylen;
1081+
return RNP_SUCCESS;
1082+
}
1083+
#endif
1084+
9731085
rnp_result_t
9741086
pgp_sk_sesskey_t::parse(pgp_source_t &src)
9751087
{
@@ -982,6 +1094,12 @@ pgp_sk_sesskey_t::parse(pgp_source_t &src)
9821094
/* version */
9831095
uint8_t bt;
9841096
if (!pkt.get(bt) || ((bt != PGP_SKSK_V4) && (bt != PGP_SKSK_V5))) {
1097+
#if defined(ENABLE_CRYPTO_REFRESH)
1098+
if (bt == PGP_SKSK_V6) {
1099+
version = bt;
1100+
return parse_v6(pkt);
1101+
}
1102+
#endif
9851103
RNP_LOG("wrong packet version");
9861104
return RNP_ERROR_BAD_FORMAT;
9871105
}
@@ -1149,7 +1267,7 @@ pgp_pk_sesskey_t::parse(pgp_source_t &src)
11491267
return RNP_ERROR_BAD_FORMAT;
11501268
}
11511269
fp.length = fp_len;
1152-
if (fp.length && (fp.length != (unsigned)(fp_and_key_ver_len - 1))) {
1270+
if (fp.length && (fp.length != (unsigned) (fp_and_key_ver_len - 1))) {
11531271
RNP_LOG("size mismatch (fingerprint size and fp+key version length field)");
11541272
return RNP_ERROR_BAD_FORMAT;
11551273
}

src/librepgp/stream-packet.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ typedef struct pgp_sk_sesskey_t {
211211

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

216221
/** pgp_one_pass_sig_t */

src/librepgp/stream-parse.cpp

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,7 +1780,11 @@ encrypted_try_password(pgp_source_encrypted_param_t *param, const char *password
17801780
continue;
17811781
}
17821782
keyavail = true;
1783-
} else if (skey.version == PGP_SKSK_V5) {
1783+
} else if (skey.version == PGP_SKSK_V5
1784+
#if defined(ENABLE_CRYPTO_REFRESH)
1785+
|| skey.version == PGP_SKSK_V6
1786+
#endif
1787+
) {
17841788
#if !defined(ENABLE_AEAD)
17851789
continue;
17861790
#else
@@ -1794,6 +1798,31 @@ encrypted_try_password(pgp_source_encrypted_param_t *param, const char *password
17941798
alg = skey.alg;
17951799

17961800
/* initialize cipher */
1801+
#if defined(ENABLE_CRYPTO_REFRESH)
1802+
if (skey.version == PGP_SKSK_V6) {
1803+
/* For v6 SKESK, we use the S2K derived key as input to the KDF */
1804+
auto kdf = rnp::Hkdf::create(PGP_HASH_SHA256);
1805+
1806+
std::vector<uint8_t> kdf_info;
1807+
kdf_info.push_back(PGP_PKT_SK_SESSION_KEY | PGP_PTAG_ALWAYS_SET |
1808+
PGP_PTAG_NEW_FORMAT);
1809+
kdf_info.push_back(skey.version);
1810+
kdf_info.push_back(skey.alg);
1811+
kdf_info.push_back(skey.aalg);
1812+
1813+
std::vector<uint8_t> kdf_input(keybuf.data(),
1814+
keybuf.data() + pgp_key_size(skey.alg));
1815+
1816+
kdf->extract_expand(NULL,
1817+
0, // no salt
1818+
kdf_input.data(),
1819+
kdf_input.size(),
1820+
kdf_info.data(),
1821+
kdf_info.size(),
1822+
keybuf.data(),
1823+
keybuf.size());
1824+
}
1825+
#endif
17971826
if (!pgp_cipher_aead_init(&crypt, skey.alg, skey.aalg, keybuf.data(), true)) {
17981827
continue;
17991828
}
@@ -2241,14 +2270,12 @@ encrypted_read_packet_data(pgp_source_encrypted_param_t *param)
22412270
}
22422271
#ifdef ENABLE_CRYPTO_REFRESH
22432272
else if (SEIPD_version == PGP_SE_IP_DATA_V2) {
2244-
/* SKESK v6 is not yet implemented, thus we must not attempt to decrypt
2245-
SEIPDv2 here
2246-
TODO: Once SKESK v6 is implemented, replace this check with a check for
2247-
consistency between SEIPD and SKESK version
2248-
*/
2249-
if (param->symencs.size() > 0) {
2250-
RNP_LOG("SEIPDv2 not usable with SKESK version");
2251-
return RNP_ERROR_BAD_FORMAT;
2273+
for (auto symenc : param->symencs) {
2274+
// consistency check if SEIPDv2 is only coupled with SKESKv6
2275+
if (symenc.version != PGP_SKSK_V6) {
2276+
RNP_LOG("SEIPDv2 not usable with SKESK version");
2277+
return RNP_ERROR_BAD_FORMAT;
2278+
}
22522279
}
22532280

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

src/librepgp/stream-write.cpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include <time.h>
5858
#include <algorithm>
5959
#ifdef ENABLE_CRYPTO_REFRESH
60+
#include "crypto/hkdf.hpp"
6061
#include "v2_seipd.h"
6162
#endif
6263

@@ -721,7 +722,11 @@ encrypted_add_password(rnp_symmetric_pass_info_t * pass,
721722

722723
skey.s2k = pass->s2k;
723724

724-
if (param->auth_type != rnp::AuthType::AEADv1) {
725+
if (param->auth_type != rnp::AuthType::AEADv1
726+
#if defined(ENABLE_CRYPTO_REFRESH)
727+
&& param->auth_type != rnp::AuthType::AEADv2
728+
#endif
729+
) {
725730
skey.version = PGP_SKSK_V4;
726731
if (singlepass) {
727732
/* if there are no public keys then we do not encrypt session key in the packet */
@@ -758,6 +763,33 @@ encrypted_add_password(rnp_symmetric_pass_info_t * pass,
758763
skey.ivlen = pgp_cipher_aead_nonce_len(skey.aalg);
759764
skey.enckeylen = keylen + pgp_cipher_aead_tag_len(skey.aalg);
760765

766+
#if defined(ENABLE_CRYPTO_REFRESH)
767+
if (param->auth_type == rnp::AuthType::AEADv2) {
768+
skey.version = PGP_SKSK_V6;
769+
770+
auto kdf = rnp::Hkdf::create(PGP_HASH_SHA256);
771+
772+
std::vector<uint8_t> kdf_info;
773+
kdf_info.push_back(PGP_PKT_SK_SESSION_KEY | PGP_PTAG_ALWAYS_SET |
774+
PGP_PTAG_NEW_FORMAT);
775+
kdf_info.push_back(skey.version);
776+
kdf_info.push_back(skey.alg);
777+
kdf_info.push_back(skey.aalg);
778+
779+
std::vector<uint8_t> kdf_input(pass->key.data(),
780+
pass->key.data() + pgp_key_size(skey.alg));
781+
782+
kdf->extract_expand(NULL,
783+
0, // no salt
784+
kdf_input.data(),
785+
kdf_input.size(),
786+
kdf_info.data(),
787+
kdf_info.size(),
788+
pass->key.data(),
789+
pgp_key_size(skey.alg));
790+
}
791+
#endif
792+
761793
try {
762794
param->ctx->ctx->rng.get(skey.iv, skey.ivlen);
763795
} catch (const std::exception &e) {
@@ -1012,6 +1044,12 @@ init_encrypted_dst(pgp_write_handler_t *handler, pgp_dest_t *dst, pgp_dest_t *wr
10121044
if (handler->ctx->enable_pkesk_v6 && handler->ctx->pkeskv6_capable() && pkeycount > 0) {
10131045
param->auth_type = rnp::AuthType::AEADv2;
10141046
}
1047+
1048+
/* Use SEIPDv2 for SKESK if enabled and preconditions are met */
1049+
if (handler->ctx->enable_skesk_v6 && handler->ctx->aalg != PGP_AEAD_NONE &&
1050+
skeycount > 0) {
1051+
param->auth_type = rnp::AuthType::AEADv2;
1052+
}
10151053
#endif
10161054
param->aalg = handler->ctx->aalg;
10171055
param->ctx = handler->ctx;

0 commit comments

Comments
 (0)