Skip to content

Commit 4e365aa

Browse files
committed
add v6 SKESK
1 parent b745533 commit 4e365aa

20 files changed

+362
-25
lines changed

include/repgp/repgp_def.h

+7-1
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

+11
Original file line numberDiff line numberDiff line change
@@ -3164,6 +3164,17 @@ RNP_API rnp_result_t rnp_op_encrypt_add_recipient(rnp_op_encrypt_t op, rnp_key_h
31643164
* @return RNP_SUCCESS or errorcode if failed.
31653165
*/
31663166
RNP_API rnp_result_t rnp_op_encrypt_enable_pkesk_v6(rnp_op_encrypt_t op);
3167+
3168+
/**
3169+
* @brief Enables the creation of SKESK v6 (instead of v4) which results in the use of SEIPDv2.
3170+
* The actually created version depends on whether an AEAD algorithm has been chosen.
3171+
* NOTE: This is an experimental feature and this function can be replaced (or removed)
3172+
* at any time.
3173+
*
3174+
* @param op opaque encrypting context. Must be allocated and initialized.
3175+
* @return RNP_SUCCESS or errorcode if failed.
3176+
*/
3177+
RNP_API rnp_result_t rnp_op_encrypt_enable_skesk_v6(rnp_op_encrypt_t op);
31673178
#endif
31683179

31693180
/**

src/lib/rnp.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -2590,6 +2590,18 @@ try {
25902590
return RNP_SUCCESS;
25912591
}
25922592
FFI_GUARD
2593+
2594+
rnp_result_t
2595+
rnp_op_encrypt_enable_skesk_v6(rnp_op_encrypt_t op)
2596+
try {
2597+
if (!op) {
2598+
return RNP_ERROR_NULL_POINTER;
2599+
}
2600+
2601+
op->rnpctx.enable_skesk_v6 = true;
2602+
return RNP_SUCCESS;
2603+
}
2604+
FFI_GUARD
25932605
#endif
25942606

25952607
rnp_result_t
@@ -2720,7 +2732,7 @@ try {
27202732
return RNP_ERROR_BAD_PARAMETERS;
27212733
}
27222734
#ifdef ENABLE_CRYPTO_REFRESH
2723-
if (op->rnpctx.aalg == PGP_AEAD_NONE && op->rnpctx.enable_pkesk_v6) {
2735+
if (op->rnpctx.aalg == PGP_AEAD_NONE && (op->rnpctx.enable_pkesk_v6)) {
27242736
FFI_LOG(op->ffi,
27252737
"Setting AEAD algorithm to PGP_AEAD_NONE (%s) would contradict the previously "
27262738
"enabled PKESKv6 setting",

src/librepgp/stream-ctx.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ typedef struct rnp_symmetric_pass_info_t {
7070
* - halg : hash algorithm used during key derivation for password-based encryption
7171
* - ealg, aalg, abits : symmetric encryption algorithm and AEAD parameters if used
7272
* - recipients : list of key ids used to encrypt data to
73-
* - enable_pkesk_v6 : if true and each recipient in the list of recipients has the
74-
* capability, allows PKESKv5/SEIPDv2
73+
* - enable_pkesk_v6 : if true and each recipient in the list of recipients, creates
74+
* PKESKv6/SEIPDv2
75+
* - enable_skesk_v6 : if true and AEAD cipher is chosen, creates SKESKv6/SEIPDv2
7576
* - passwords : list of passwords used for password-based encryption
7677
* - filename, filemtime, zalg, zlevel : see previous
7778
* - pkeskv6_capable() : returns true if all keys support PKESKv6+SEIPDv2, false otherwise
@@ -108,6 +109,7 @@ typedef struct rnp_ctx_t {
108109
bool no_wrap{}; /* do not wrap source in literal data packet */
109110
#if defined(ENABLE_CRYPTO_REFRESH)
110111
bool enable_pkesk_v6{}; /* allows pkesk v6 if list of recipients is suitable */
112+
bool enable_skesk_v6{}; /* allows skesk v6 if chosen cipher is suitable */
111113
#endif
112114
std::list<pgp_key_t *> recipients{}; /* recipients of the encrypted message */
113115
std::list<rnp_symmetric_pass_info_t> passwords{}; /* passwords to encrypt message */

src/librepgp/stream-packet.cpp

+122-4
Original file line numberDiff line numberDiff line change
@@ -921,11 +921,43 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
921921
pgp_packet_body_t pktbody(PGP_PKT_SK_SESSION_KEY);
922922
/* version and algorithm fields */
923923
pktbody.add_byte(version);
924+
#if defined(ENABLE_CRYPTO_REFRESH)
925+
uint8_t s2k_len;
926+
/* A one-octet scalar octet count for the 5 fields following this octet. */
927+
/* TODO: unify with pgp_key_pkt_t::s2k_specifier_len() */
928+
if (version == PGP_SKSK_V6) {
929+
switch (s2k.specifier) {
930+
case PGP_S2KS_SIMPLE:
931+
s2k_len = 2;
932+
break;
933+
case PGP_S2KS_SALTED:
934+
s2k_len = 10;
935+
break;
936+
case PGP_S2KS_ITERATED_AND_SALTED:
937+
s2k_len = 11;
938+
break;
939+
default:
940+
RNP_LOG("invalid specifier");
941+
throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
942+
}
943+
pktbody.add_byte(3 + s2k_len + ivlen);
944+
}
945+
#endif
924946
pktbody.add_byte(alg);
925-
if (version == PGP_SKSK_V5) {
947+
if (version == PGP_SKSK_V5
948+
#if defined(ENABLE_CRYPTO_REFRESH)
949+
|| version == PGP_SKSK_V6
950+
#endif
951+
) {
926952
pktbody.add_byte(aalg);
927953
}
928-
/* S2K specifier */
954+
/* S2K specifier */
955+
#if defined(ENABLE_CRYPTO_REFRESH)
956+
/* A one-octet scalar octet count of the following field. */
957+
if (version == PGP_SKSK_V6) {
958+
pktbody.add_byte(s2k_len);
959+
}
960+
#endif
929961
pktbody.add_byte(s2k.specifier);
930962
pktbody.add_byte(s2k.hash_alg);
931963

@@ -944,7 +976,11 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
944976
throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
945977
}
946978
/* v5 : iv */
947-
if (version == PGP_SKSK_V5) {
979+
if (version == PGP_SKSK_V5
980+
#if defined(ENABLE_CRYPTO_REFRESH)
981+
|| version == PGP_SKSK_V6
982+
#endif
983+
) {
948984
pktbody.add(iv, ivlen);
949985
}
950986
/* encrypted key and auth tag for v5 */
@@ -955,6 +991,82 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
955991
pktbody.write(dst);
956992
}
957993

994+
#if defined(ENABLE_CRYPTO_REFRESH)
995+
rnp_result_t
996+
pgp_sk_sesskey_t::parse_v6(pgp_packet_body_t &pkt)
997+
{
998+
uint8_t bt;
999+
uint8_t octet_count;
1000+
uint8_t s2k_len;
1001+
1002+
/* A one-octet scalar octet count for the 5 fields following this octet. */
1003+
/* TODO: do we need to check octet_count? */
1004+
if (!pkt.get(octet_count)) {
1005+
RNP_LOG("failed to get octet count of next 5 fields");
1006+
return RNP_ERROR_BAD_FORMAT;
1007+
}
1008+
1009+
/* symmetric algorithm */
1010+
if (!pkt.get(bt)) {
1011+
RNP_LOG("failed to get symm alg");
1012+
return RNP_ERROR_BAD_FORMAT;
1013+
}
1014+
alg = (pgp_symm_alg_t) bt;
1015+
1016+
/* aead algorithm */
1017+
if (!pkt.get(bt)) {
1018+
RNP_LOG("failed to get aead alg");
1019+
return RNP_ERROR_BAD_FORMAT;
1020+
}
1021+
aalg = (pgp_aead_alg_t) bt;
1022+
if ((aalg != PGP_AEAD_EAX) && (aalg != PGP_AEAD_OCB)) {
1023+
RNP_LOG("unsupported AEAD algorithm : %d", (int) aalg);
1024+
return RNP_ERROR_BAD_PARAMETERS;
1025+
}
1026+
1027+
/* A one-octet scalar octet count of the following field. */
1028+
/* TODO: do we need to check s2k_len? */
1029+
if (!pkt.get(s2k_len)) {
1030+
RNP_LOG("failed to get octet count of next 5 fields");
1031+
return RNP_ERROR_BAD_FORMAT;
1032+
}
1033+
1034+
/* s2k */
1035+
if (!pkt.get(s2k)) {
1036+
RNP_LOG("failed to parse s2k");
1037+
return RNP_ERROR_BAD_FORMAT;
1038+
}
1039+
1040+
size_t noncelen = pgp_cipher_aead_nonce_len(aalg);
1041+
size_t taglen = pgp_cipher_aead_tag_len(aalg);
1042+
size_t keylen = 0;
1043+
1044+
if (pkt.left() > noncelen + taglen + PGP_MAX_KEY_SIZE) {
1045+
RNP_LOG("too long esk");
1046+
return RNP_ERROR_BAD_FORMAT;
1047+
}
1048+
if (pkt.left() < noncelen + taglen + 8) {
1049+
RNP_LOG("too short esk");
1050+
return RNP_ERROR_BAD_FORMAT;
1051+
}
1052+
/* iv */
1053+
if (!pkt.get(iv, noncelen)) {
1054+
RNP_LOG("failed to get iv");
1055+
return RNP_ERROR_BAD_FORMAT;
1056+
}
1057+
ivlen = noncelen;
1058+
1059+
/* key */
1060+
keylen = pkt.left();
1061+
if (!pkt.get(enckey, keylen)) {
1062+
RNP_LOG("failed to get key");
1063+
return RNP_ERROR_BAD_FORMAT;
1064+
}
1065+
enckeylen = keylen;
1066+
return RNP_SUCCESS;
1067+
}
1068+
#endif
1069+
9581070
rnp_result_t
9591071
pgp_sk_sesskey_t::parse(pgp_source_t &src)
9601072
{
@@ -967,6 +1079,12 @@ pgp_sk_sesskey_t::parse(pgp_source_t &src)
9671079
/* version */
9681080
uint8_t bt;
9691081
if (!pkt.get(bt) || ((bt != PGP_SKSK_V4) && (bt != PGP_SKSK_V5))) {
1082+
#if defined(ENABLE_CRYPTO_REFRESH)
1083+
if (bt == PGP_SKSK_V6) {
1084+
version = bt;
1085+
return parse_v6(pkt);
1086+
}
1087+
#endif
9701088
RNP_LOG("wrong packet version");
9711089
return RNP_ERROR_BAD_FORMAT;
9721090
}
@@ -1134,7 +1252,7 @@ pgp_pk_sesskey_t::parse(pgp_source_t &src)
11341252
return RNP_ERROR_BAD_FORMAT;
11351253
}
11361254
fp.length = fp_len;
1137-
if (fp.length && (fp.length != (unsigned)(fp_and_key_ver_len - 1))) {
1255+
if (fp.length && (fp.length != (unsigned) (fp_and_key_ver_len - 1))) {
11381256
RNP_LOG("size mismatch (fingerprint size and fp+key version length field)");
11391257
return RNP_ERROR_BAD_FORMAT;
11401258
}

src/librepgp/stream-packet.h

+5
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ typedef struct pgp_sk_sesskey_t {
204204

205205
void write(pgp_dest_t &dst) const;
206206
rnp_result_t parse(pgp_source_t &src);
207+
208+
#if defined(ENABLE_CRYPTO_REFRESH)
209+
private:
210+
rnp_result_t parse_v6(pgp_packet_body_t &pkt);
211+
#endif
207212
} pgp_sk_sesskey_t;
208213

209214
/** pgp_one_pass_sig_t */

src/librepgp/stream-parse.cpp

+36-9
Original file line numberDiff line numberDiff line change
@@ -1828,7 +1828,11 @@ encrypted_try_password(pgp_source_encrypted_param_t *param, const char *password
18281828
continue;
18291829
}
18301830
keyavail = true;
1831-
} else if (skey.version == PGP_SKSK_V5) {
1831+
} else if (skey.version == PGP_SKSK_V5
1832+
#if defined(ENABLE_CRYPTO_REFRESH)
1833+
|| skey.version == PGP_SKSK_V6
1834+
#endif
1835+
) {
18321836
#if !defined(ENABLE_AEAD)
18331837
continue;
18341838
#else
@@ -1842,6 +1846,31 @@ encrypted_try_password(pgp_source_encrypted_param_t *param, const char *password
18421846
alg = skey.alg;
18431847

18441848
/* initialize cipher */
1849+
#if defined(ENABLE_CRYPTO_REFRESH)
1850+
if (skey.version == PGP_SKSK_V6) {
1851+
/* For v6 SKESK, we use the S2K derived key as input to the KDF */
1852+
auto kdf = rnp::Hkdf::create(PGP_HASH_SHA256);
1853+
1854+
std::vector<uint8_t> kdf_info;
1855+
kdf_info.push_back(PGP_PKT_SK_SESSION_KEY | PGP_PTAG_ALWAYS_SET |
1856+
PGP_PTAG_NEW_FORMAT);
1857+
kdf_info.push_back(skey.version);
1858+
kdf_info.push_back(skey.alg);
1859+
kdf_info.push_back(skey.aalg);
1860+
1861+
std::vector<uint8_t> kdf_input(keybuf.data(),
1862+
keybuf.data() + pgp_key_size(skey.alg));
1863+
1864+
kdf->extract_expand(NULL,
1865+
0, // no salt
1866+
kdf_input.data(),
1867+
kdf_input.size(),
1868+
kdf_info.data(),
1869+
kdf_info.size(),
1870+
keybuf.data(),
1871+
keybuf.size());
1872+
}
1873+
#endif
18451874
if (!pgp_cipher_aead_init(&crypt, skey.alg, skey.aalg, keybuf.data(), true)) {
18461875
continue;
18471876
}
@@ -2285,14 +2314,12 @@ encrypted_read_packet_data(pgp_source_encrypted_param_t *param)
22852314
}
22862315
#ifdef ENABLE_CRYPTO_REFRESH
22872316
else if (SEIPD_version == PGP_SE_IP_DATA_V2) {
2288-
/* SKESK v6 is not yet implemented, thus we must not attempt to decrypt
2289-
SEIPDv2 here
2290-
TODO: Once SKESK v6 is implemented, replace this check with a check for
2291-
consistency between SEIPD and SKESK version
2292-
*/
2293-
if (param->symencs.size() > 0) {
2294-
RNP_LOG("SEIPDv2 not usable with SKESK version");
2295-
return RNP_ERROR_BAD_FORMAT;
2317+
for (auto symenc : param->symencs) {
2318+
// consistency check if SEIPDv2 is only coupled with SKESKv6
2319+
if (symenc.version != PGP_SKSK_V6) {
2320+
RNP_LOG("SEIPDv2 not usable with SKESK version");
2321+
return RNP_ERROR_BAD_FORMAT;
2322+
}
22962323
}
22972324

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

src/librepgp/stream-write.cpp

+39-1
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

@@ -784,7 +785,11 @@ encrypted_add_password(rnp_symmetric_pass_info_t * pass,
784785

785786
skey.s2k = pass->s2k;
786787

787-
if (param->auth_type != rnp::AuthType::AEADv1) {
788+
if (param->auth_type != rnp::AuthType::AEADv1
789+
#if defined(ENABLE_CRYPTO_REFRESH)
790+
&& param->auth_type != rnp::AuthType::AEADv2
791+
#endif
792+
) {
788793
skey.version = PGP_SKSK_V4;
789794
if (singlepass) {
790795
/* if there are no public keys then we do not encrypt session key in the packet */
@@ -821,6 +826,33 @@ encrypted_add_password(rnp_symmetric_pass_info_t * pass,
821826
skey.ivlen = pgp_cipher_aead_nonce_len(skey.aalg);
822827
skey.enckeylen = keylen + pgp_cipher_aead_tag_len(skey.aalg);
823828

829+
#if defined(ENABLE_CRYPTO_REFRESH)
830+
if (param->auth_type == rnp::AuthType::AEADv2) {
831+
skey.version = PGP_SKSK_V6;
832+
833+
auto kdf = rnp::Hkdf::create(PGP_HASH_SHA256);
834+
835+
std::vector<uint8_t> kdf_info;
836+
kdf_info.push_back(PGP_PKT_SK_SESSION_KEY | PGP_PTAG_ALWAYS_SET |
837+
PGP_PTAG_NEW_FORMAT);
838+
kdf_info.push_back(skey.version);
839+
kdf_info.push_back(skey.alg);
840+
kdf_info.push_back(skey.aalg);
841+
842+
std::vector<uint8_t> kdf_input(pass->key.data(),
843+
pass->key.data() + pgp_key_size(skey.alg));
844+
845+
kdf->extract_expand(NULL,
846+
0, // no salt
847+
kdf_input.data(),
848+
kdf_input.size(),
849+
kdf_info.data(),
850+
kdf_info.size(),
851+
pass->key.data(),
852+
pgp_key_size(skey.alg));
853+
}
854+
#endif
855+
824856
try {
825857
param->ctx->ctx->rng.get(skey.iv, skey.ivlen);
826858
} catch (const std::exception &e) {
@@ -1063,6 +1095,12 @@ init_encrypted_dst(pgp_write_handler_t *handler, pgp_dest_t *dst, pgp_dest_t *wr
10631095
if (handler->ctx->enable_pkesk_v6 && handler->ctx->pkeskv6_capable() && pkeycount > 0) {
10641096
param->auth_type = rnp::AuthType::AEADv2;
10651097
}
1098+
1099+
/* Use SEIPDv2 for SKESK if enabled and preconditions are met */
1100+
if (handler->ctx->enable_skesk_v6 && handler->ctx->aalg != PGP_AEAD_NONE &&
1101+
skeycount > 0) {
1102+
param->auth_type = rnp::AuthType::AEADv2;
1103+
}
10661104
#endif
10671105
param->aalg = handler->ctx->aalg;
10681106
param->ctx = handler->ctx;

0 commit comments

Comments
 (0)