Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add v6 SKESK #2207

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -3534,6 +3534,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 @@ -2604,6 +2604,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 @@ -2734,7 +2746,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
130 changes: 124 additions & 6 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 != 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 Expand Up @@ -1245,7 +1363,7 @@ pgp_pk_sesskey_t::parse_material(pgp_encrypted_material_t &material)
if ((version == PGP_PKSK_V3) && !do_encrypt_pkesk_v3_alg_id(alg)) {
if (!pkt.get(bt)) {
RNP_LOG("failed to get salg");
return RNP_ERROR_BAD_FORMAT;
return false;
}
enc_sesskey_len -= 1;
salg = (pgp_symm_alg_t) bt;
Expand Down Expand Up @@ -1286,7 +1404,7 @@ pgp_pk_sesskey_t::parse_material(pgp_encrypted_material_t &material)
if ((version == PGP_PKSK_V3) && !do_encrypt_pkesk_v3_alg_id(alg)) {
if (!pkt.get(bt)) {
RNP_LOG("failed to get salg");
return RNP_ERROR_BAD_FORMAT;
return false;
}
salg = (pgp_symm_alg_t) bt;
wrapped_key_len--;
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 @@ -1781,7 +1781,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 @@ -1795,6 +1799,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 @@ -2242,14 +2271,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
Loading
Loading