Skip to content

Commit

Permalink
add v6 SKESK
Browse files Browse the repository at this point in the history
  • Loading branch information
TJ-91 committed Mar 26, 2024
1 parent b745533 commit 4e365aa
Show file tree
Hide file tree
Showing 20 changed files with 362 additions and 25 deletions.
8 changes: 7 additions & 1 deletion include/repgp/repgp_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,13 @@ typedef enum {
PGP_C_UNKNOWN = 255
} pgp_compression_type_t;

enum { PGP_SKSK_V4 = 4, PGP_SKSK_V5 = 5 };
typedef enum {
PGP_SKSK_V4 = 4,
PGP_SKSK_V5 = 5,
#if defined(ENABLE_CRYPTO_REFRESH)
PGP_SKSK_V6 = 6
#endif
} pgp_skesk_version_t;
typedef enum {
PGP_PKSK_V3 = 3,
#if defined(ENABLE_CRYPTO_REFRESH)
Expand Down
11 changes: 11 additions & 0 deletions include/rnp/rnp.h
Original file line number Diff line number Diff line change
Expand Up @@ -3164,6 +3164,17 @@ RNP_API rnp_result_t rnp_op_encrypt_add_recipient(rnp_op_encrypt_t op, rnp_key_h
* @return RNP_SUCCESS or errorcode if failed.
*/
RNP_API rnp_result_t rnp_op_encrypt_enable_pkesk_v6(rnp_op_encrypt_t op);

/**
* @brief Enables the creation of SKESK v6 (instead of v4) which results in the use of SEIPDv2.
* The actually created version depends on whether an AEAD algorithm has been chosen.
* 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_enable_skesk_v6(rnp_op_encrypt_t op);
#endif

/**
Expand Down
14 changes: 13 additions & 1 deletion src/lib/rnp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2590,6 +2590,18 @@ try {
return RNP_SUCCESS;
}
FFI_GUARD

rnp_result_t
rnp_op_encrypt_enable_skesk_v6(rnp_op_encrypt_t op)
try {
if (!op) {
return RNP_ERROR_NULL_POINTER;
}

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

rnp_result_t
Expand Down Expand Up @@ -2720,7 +2732,7 @@ try {
return RNP_ERROR_BAD_PARAMETERS;
}
#ifdef ENABLE_CRYPTO_REFRESH
if (op->rnpctx.aalg == PGP_AEAD_NONE && op->rnpctx.enable_pkesk_v6) {
if (op->rnpctx.aalg == PGP_AEAD_NONE && (op->rnpctx.enable_pkesk_v6)) {
FFI_LOG(op->ffi,
"Setting AEAD algorithm to PGP_AEAD_NONE (%s) would contradict the previously "
"enabled PKESKv6 setting",
Expand Down
6 changes: 4 additions & 2 deletions src/librepgp/stream-ctx.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ 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 : if true and each recipient in the list of recipients, creates
* PKESKv6/SEIPDv2
* - enable_skesk_v6 : if true and AEAD cipher is chosen, creates SKESKv6/SEIPDv2
* - 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 +109,7 @@ 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 */
bool enable_skesk_v6{}; /* allows skesk v6 if chosen cipher is suitable */
#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
126 changes: 122 additions & 4 deletions src/librepgp/stream-packet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -921,11 +921,43 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
pgp_packet_body_t pktbody(PGP_PKT_SK_SESSION_KEY);
/* version and algorithm fields */
pktbody.add_byte(version);
#if defined(ENABLE_CRYPTO_REFRESH)
uint8_t s2k_len;
/* A one-octet scalar octet count for the 5 fields following this octet. */
/* TODO: unify with pgp_key_pkt_t::s2k_specifier_len() */
if (version == PGP_SKSK_V6) {
switch (s2k.specifier) {
case PGP_S2KS_SIMPLE:
s2k_len = 2;
break;
case PGP_S2KS_SALTED:
s2k_len = 10;
break;
case PGP_S2KS_ITERATED_AND_SALTED:
s2k_len = 11;
break;
default:
RNP_LOG("invalid specifier");
throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
}
pktbody.add_byte(3 + s2k_len + ivlen);
}
#endif
pktbody.add_byte(alg);
if (version == PGP_SKSK_V5) {
if (version == PGP_SKSK_V5
#if defined(ENABLE_CRYPTO_REFRESH)
|| version == PGP_SKSK_V6
#endif
) {
pktbody.add_byte(aalg);
}
/* S2K specifier */
/* S2K specifier */
#if defined(ENABLE_CRYPTO_REFRESH)
/* A one-octet scalar octet count of the following field. */
if (version == PGP_SKSK_V6) {
pktbody.add_byte(s2k_len);
}
#endif
pktbody.add_byte(s2k.specifier);
pktbody.add_byte(s2k.hash_alg);

Expand All @@ -944,7 +976,11 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
}
/* v5 : iv */
if (version == PGP_SKSK_V5) {
if (version == PGP_SKSK_V5
#if defined(ENABLE_CRYPTO_REFRESH)
|| version == PGP_SKSK_V6
#endif
) {
pktbody.add(iv, ivlen);
}
/* encrypted key and auth tag for v5 */
Expand All @@ -955,6 +991,82 @@ pgp_sk_sesskey_t::write(pgp_dest_t &dst) const
pktbody.write(dst);
}

#if defined(ENABLE_CRYPTO_REFRESH)
rnp_result_t
pgp_sk_sesskey_t::parse_v6(pgp_packet_body_t &pkt)
{
uint8_t bt;
uint8_t octet_count;
uint8_t s2k_len;

/* A one-octet scalar octet count for the 5 fields following this octet. */
/* TODO: do we need to check octet_count? */
if (!pkt.get(octet_count)) {
RNP_LOG("failed to get octet count of next 5 fields");
return RNP_ERROR_BAD_FORMAT;
}

/* symmetric algorithm */
if (!pkt.get(bt)) {
RNP_LOG("failed to get symm alg");
return RNP_ERROR_BAD_FORMAT;
}
alg = (pgp_symm_alg_t) bt;

/* aead algorithm */
if (!pkt.get(bt)) {
RNP_LOG("failed to get aead alg");
return RNP_ERROR_BAD_FORMAT;
}
aalg = (pgp_aead_alg_t) bt;
if ((aalg != PGP_AEAD_EAX) && (aalg != PGP_AEAD_OCB)) {
RNP_LOG("unsupported AEAD algorithm : %d", (int) aalg);
return RNP_ERROR_BAD_PARAMETERS;
}

/* A one-octet scalar octet count of the following field. */
/* TODO: do we need to check s2k_len? */
if (!pkt.get(s2k_len)) {
RNP_LOG("failed to get octet count of next 5 fields");
return RNP_ERROR_BAD_FORMAT;
}

/* s2k */
if (!pkt.get(s2k)) {
RNP_LOG("failed to parse s2k");
return RNP_ERROR_BAD_FORMAT;
}

size_t noncelen = pgp_cipher_aead_nonce_len(aalg);
size_t taglen = pgp_cipher_aead_tag_len(aalg);
size_t keylen = 0;

if (pkt.left() > noncelen + taglen + PGP_MAX_KEY_SIZE) {
RNP_LOG("too long esk");
return RNP_ERROR_BAD_FORMAT;
}
if (pkt.left() < noncelen + taglen + 8) {
RNP_LOG("too short esk");
return RNP_ERROR_BAD_FORMAT;
}
/* iv */
if (!pkt.get(iv, noncelen)) {
RNP_LOG("failed to get iv");
return RNP_ERROR_BAD_FORMAT;
}
ivlen = noncelen;

/* key */
keylen = pkt.left();
if (!pkt.get(enckey, keylen)) {
RNP_LOG("failed to get key");
return RNP_ERROR_BAD_FORMAT;
}
enckeylen = keylen;
return RNP_SUCCESS;
}
#endif

rnp_result_t
pgp_sk_sesskey_t::parse(pgp_source_t &src)
{
Expand All @@ -967,6 +1079,12 @@ pgp_sk_sesskey_t::parse(pgp_source_t &src)
/* version */
uint8_t bt;
if (!pkt.get(bt) || ((bt != PGP_SKSK_V4) && (bt != PGP_SKSK_V5))) {
#if defined(ENABLE_CRYPTO_REFRESH)
if (bt == PGP_SKSK_V6) {
version = bt;
return parse_v6(pkt);
}
#endif
RNP_LOG("wrong packet version");
return RNP_ERROR_BAD_FORMAT;
}
Expand Down Expand Up @@ -1134,7 +1252,7 @@ pgp_pk_sesskey_t::parse(pgp_source_t &src)
return RNP_ERROR_BAD_FORMAT;
}
fp.length = fp_len;
if (fp.length && (fp.length != (unsigned)(fp_and_key_ver_len - 1))) {
if (fp.length && (fp.length != (unsigned) (fp_and_key_ver_len - 1))) {
RNP_LOG("size mismatch (fingerprint size and fp+key version length field)");
return RNP_ERROR_BAD_FORMAT;
}
Expand Down
5 changes: 5 additions & 0 deletions src/librepgp/stream-packet.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ typedef struct pgp_sk_sesskey_t {

void write(pgp_dest_t &dst) const;
rnp_result_t parse(pgp_source_t &src);

#if defined(ENABLE_CRYPTO_REFRESH)
private:
rnp_result_t parse_v6(pgp_packet_body_t &pkt);
#endif
} pgp_sk_sesskey_t;

/** pgp_one_pass_sig_t */
Expand Down
45 changes: 36 additions & 9 deletions src/librepgp/stream-parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1828,7 +1828,11 @@ encrypted_try_password(pgp_source_encrypted_param_t *param, const char *password
continue;
}
keyavail = true;
} else if (skey.version == PGP_SKSK_V5) {
} else if (skey.version == PGP_SKSK_V5
#if defined(ENABLE_CRYPTO_REFRESH)
|| skey.version == PGP_SKSK_V6
#endif
) {
#if !defined(ENABLE_AEAD)
continue;
#else
Expand All @@ -1842,6 +1846,31 @@ encrypted_try_password(pgp_source_encrypted_param_t *param, const char *password
alg = skey.alg;

/* initialize cipher */
#if defined(ENABLE_CRYPTO_REFRESH)
if (skey.version == PGP_SKSK_V6) {
/* For v6 SKESK, we use the S2K derived key as input to the KDF */
auto kdf = rnp::Hkdf::create(PGP_HASH_SHA256);

std::vector<uint8_t> kdf_info;
kdf_info.push_back(PGP_PKT_SK_SESSION_KEY | PGP_PTAG_ALWAYS_SET |
PGP_PTAG_NEW_FORMAT);
kdf_info.push_back(skey.version);
kdf_info.push_back(skey.alg);
kdf_info.push_back(skey.aalg);

std::vector<uint8_t> kdf_input(keybuf.data(),
keybuf.data() + pgp_key_size(skey.alg));

kdf->extract_expand(NULL,
0, // no salt
kdf_input.data(),
kdf_input.size(),
kdf_info.data(),
kdf_info.size(),
keybuf.data(),
keybuf.size());
}
#endif
if (!pgp_cipher_aead_init(&crypt, skey.alg, skey.aalg, keybuf.data(), true)) {
continue;
}
Expand Down Expand Up @@ -2285,14 +2314,12 @@ encrypted_read_packet_data(pgp_source_encrypted_param_t *param)
}
#ifdef ENABLE_CRYPTO_REFRESH
else if (SEIPD_version == PGP_SE_IP_DATA_V2) {
/* SKESK v6 is not yet implemented, thus we must not attempt to decrypt
SEIPDv2 here
TODO: Once SKESK v6 is implemented, replace this check with a check for
consistency between SEIPD and SKESK version
*/
if (param->symencs.size() > 0) {
RNP_LOG("SEIPDv2 not usable with SKESK version");
return RNP_ERROR_BAD_FORMAT;
for (auto symenc : param->symencs) {
// consistency check if SEIPDv2 is only coupled with SKESKv6
if (symenc.version != PGP_SKSK_V6) {
RNP_LOG("SEIPDv2 not usable with SKESK version");
return RNP_ERROR_BAD_FORMAT;
}
}

param->auth_type = rnp::AuthType::AEADv2;
Expand Down
40 changes: 39 additions & 1 deletion src/librepgp/stream-write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include <time.h>
#include <algorithm>
#ifdef ENABLE_CRYPTO_REFRESH
#include "crypto/hkdf.hpp"
#include "v2_seipd.h"
#endif

Expand Down Expand Up @@ -784,7 +785,11 @@ encrypted_add_password(rnp_symmetric_pass_info_t * pass,

skey.s2k = pass->s2k;

if (param->auth_type != rnp::AuthType::AEADv1) {
if (param->auth_type != rnp::AuthType::AEADv1
#if defined(ENABLE_CRYPTO_REFRESH)
&& param->auth_type != rnp::AuthType::AEADv2
#endif
) {
skey.version = PGP_SKSK_V4;
if (singlepass) {
/* if there are no public keys then we do not encrypt session key in the packet */
Expand Down Expand Up @@ -821,6 +826,33 @@ encrypted_add_password(rnp_symmetric_pass_info_t * pass,
skey.ivlen = pgp_cipher_aead_nonce_len(skey.aalg);
skey.enckeylen = keylen + pgp_cipher_aead_tag_len(skey.aalg);

#if defined(ENABLE_CRYPTO_REFRESH)
if (param->auth_type == rnp::AuthType::AEADv2) {
skey.version = PGP_SKSK_V6;

auto kdf = rnp::Hkdf::create(PGP_HASH_SHA256);

std::vector<uint8_t> kdf_info;
kdf_info.push_back(PGP_PKT_SK_SESSION_KEY | PGP_PTAG_ALWAYS_SET |
PGP_PTAG_NEW_FORMAT);
kdf_info.push_back(skey.version);
kdf_info.push_back(skey.alg);
kdf_info.push_back(skey.aalg);

std::vector<uint8_t> kdf_input(pass->key.data(),
pass->key.data() + pgp_key_size(skey.alg));

kdf->extract_expand(NULL,
0, // no salt
kdf_input.data(),
kdf_input.size(),
kdf_info.data(),
kdf_info.size(),
pass->key.data(),
pgp_key_size(skey.alg));
}
#endif

try {
param->ctx->ctx->rng.get(skey.iv, skey.ivlen);
} catch (const std::exception &e) {
Expand Down Expand Up @@ -1063,6 +1095,12 @@ init_encrypted_dst(pgp_write_handler_t *handler, pgp_dest_t *dst, pgp_dest_t *wr
if (handler->ctx->enable_pkesk_v6 && handler->ctx->pkeskv6_capable() && pkeycount > 0) {
param->auth_type = rnp::AuthType::AEADv2;
}

/* Use SEIPDv2 for SKESK if enabled and preconditions are met */
if (handler->ctx->enable_skesk_v6 && handler->ctx->aalg != PGP_AEAD_NONE &&
skeycount > 0) {
param->auth_type = rnp::AuthType::AEADv2;
}
#endif
param->aalg = handler->ctx->aalg;
param->ctx = handler->ctx;
Expand Down
Loading

0 comments on commit 4e365aa

Please sign in to comment.