diff --git a/include/repgp/repgp_def.h b/include/repgp/repgp_def.h index 524d9c62ed..68085b6e27 100644 --- a/include/repgp/repgp_def.h +++ b/include/repgp/repgp_def.h @@ -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) diff --git a/include/rnp/rnp.h b/include/rnp/rnp.h index d2d7930d8a..b43c66baec 100644 --- a/include/rnp/rnp.h +++ b/include/rnp/rnp.h @@ -3619,6 +3619,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 #if defined(RNP_EXPERIMENTAL_PQC) diff --git a/src/lib/rnp.cpp b/src/lib/rnp.cpp index 6ca0514fd4..b920dc50fb 100644 --- a/src/lib/rnp.cpp +++ b/src/lib/rnp.cpp @@ -2623,6 +2623,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 #if defined(RNP_EXPERIMENTAL_PQC) @@ -2767,7 +2779,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", diff --git a/src/librepgp/stream-ctx.h b/src/librepgp/stream-ctx.h index fa140c3132..062df8b66c 100644 --- a/src/librepgp/stream-ctx.h +++ b/src/librepgp/stream-ctx.h @@ -72,6 +72,7 @@ typedef struct rnp_symmetric_pass_info_t { * - recipients : list of key ids used to encrypt data to * - enable_pkesk_v6 (Only if defined: ENABLE_CRYPTO_REFRESH): if true and each recipient in * the list of recipients has the capability, allows PKESKv6/SEIPDv2 + * - enable_skesk_v6 : if true and AEAD cipher is chosen, creates SKESKv6/SEIPDv2 * - pref_pqc_enc_subkey (Only if defined: ENABLE_PQC): if true, prefers PQC subkey over * non-PQC subkey for encryption. * - passwords : list of passwords used for password-based encryption @@ -110,6 +111,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 #if defined(ENABLE_PQC) bool pref_pqc_enc_subkey{}; /* prefer to encrypt to PQC subkey */ diff --git a/src/librepgp/stream-packet.cpp b/src/librepgp/stream-packet.cpp index 9419436ea7..9a5ef84758 100644 --- a/src/librepgp/stream-packet.cpp +++ b/src/librepgp/stream-packet.cpp @@ -936,11 +936,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); @@ -959,7 +991,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 */ @@ -970,6 +1006,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) { @@ -982,6 +1094,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; } @@ -1149,7 +1267,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; } @@ -1260,7 +1378,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; @@ -1301,7 +1419,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--; diff --git a/src/librepgp/stream-packet.h b/src/librepgp/stream-packet.h index 25445d27cb..5960415d28 100644 --- a/src/librepgp/stream-packet.h +++ b/src/librepgp/stream-packet.h @@ -211,6 +211,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 */ diff --git a/src/librepgp/stream-parse.cpp b/src/librepgp/stream-parse.cpp index 6569d1d510..de8de5d555 100644 --- a/src/librepgp/stream-parse.cpp +++ b/src/librepgp/stream-parse.cpp @@ -1780,7 +1780,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 @@ -1794,6 +1798,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 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 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; } @@ -2241,14 +2270,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; diff --git a/src/librepgp/stream-write.cpp b/src/librepgp/stream-write.cpp index f50cd3601a..a075cbefa2 100644 --- a/src/librepgp/stream-write.cpp +++ b/src/librepgp/stream-write.cpp @@ -57,6 +57,7 @@ #include #include #ifdef ENABLE_CRYPTO_REFRESH +#include "crypto/hkdf.hpp" #include "v2_seipd.h" #endif @@ -721,7 +722,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 */ @@ -758,6 +763,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 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 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) { @@ -956,8 +988,12 @@ init_encrypted_dst(pgp_write_handler_t *handler, pgp_dest_t *dst, pgp_dest_t *wr bool singlepass = true; unsigned pkeycount = 0; unsigned skeycount = 0; - unsigned keylen; - rnp_result_t ret = RNP_ERROR_GENERIC; +#if defined(ENABLE_CRYPTO_REFRESH) + bool use_v6_pkesk = false; + bool use_v6_skesk = false; +#endif + unsigned keylen; + rnp_result_t ret = RNP_ERROR_GENERIC; keylen = pgp_key_size(handler->ctx->ealg); if (!keylen) { @@ -1007,10 +1043,15 @@ init_encrypted_dst(pgp_write_handler_t *handler, pgp_dest_t *dst, pgp_dest_t *wr skeycount = handler->ctx->passwords.size(); #if defined(ENABLE_CRYPTO_REFRESH) - /* in the case of PKESK (pkeycount > 0) and all keys are PKESKv6/SEIPDv2 capable, upgrade - * to AEADv2 */ - if (handler->ctx->enable_pkesk_v6 && handler->ctx->pkeskv6_capable() && pkeycount > 0) { - param->auth_type = rnp::AuthType::AEADv2; + /* We use v6 PKESK/SKESK with v2 SEIPD if all recipients support it + and the variables enable_pkesk_v6 and enable_skesk_v6 are set. */ + if (handler->ctx->aalg != PGP_AEAD_NONE) { + use_v6_pkesk = handler->ctx->enable_pkesk_v6 && handler->ctx->pkeskv6_capable(); + use_v6_skesk = handler->ctx->enable_skesk_v6; + + if ((use_v6_pkesk || !pkeycount) && (use_v6_skesk || !skeycount)) { + param->auth_type = rnp::AuthType::AEADv2; + } } #endif param->aalg = handler->ctx->aalg; diff --git a/src/rnp/fficli.cpp b/src/rnp/fficli.cpp index 555d957efb..78c2e3f48a 100644 --- a/src/rnp/fficli.cpp +++ b/src/rnp/fficli.cpp @@ -2822,10 +2822,14 @@ cli_rnp_encrypt_and_sign(const rnp_cfg &cfg, } #if defined(ENABLE_CRYPTO_REFRESH) - /* enable or disable v6 PKESK creation*/ + /* enable or disable v6 PKESK creation */ if (!cfg.get_bool(CFG_V3_PKESK_ONLY)) { rnp_op_encrypt_enable_pkesk_v6(op); } + /* enable or disable v6 SKESK creation */ + if (cfg.get_bool(CFG_ENABLE_V6_SKESK)) { + rnp_op_encrypt_enable_skesk_v6(op); + } #endif /* adding signatures if encrypt-and-sign is used */ diff --git a/src/rnp/rnp.cpp b/src/rnp/rnp.cpp index 8089f17b55..cfa3612ba5 100644 --- a/src/rnp/rnp.cpp +++ b/src/rnp/rnp.cpp @@ -72,6 +72,9 @@ static const char *usage = " --no-wrap Do not wrap the output in a literal data packet.\n" " -c, --symmetric Encrypt data using the password(s).\n" " --passwords num Encrypt to the specified number of passwords.\n" +#if defined(ENABLE_CRYPTO_REFRESH) + " --enable-v6-skesk Enable creation of v6 SKESK if AEAD is used.\n" +#endif " -s, --sign Sign data. May be combined with encryption.\n" " --detach Produce detached signature.\n" " -u, --userid Specify signing key(s) via uid/keyid/fingerprint.\n" @@ -132,6 +135,7 @@ enum optdefs { OPT_RECIPIENT, #if defined(ENABLE_CRYPTO_REFRESH) OPT_V3_PKESK_ONLY, + OPT_ENABLE_V6_SKESK, #endif OPT_ARMOR, OPT_HOMEDIR, @@ -200,6 +204,7 @@ static struct option options[] = { {"recipient", required_argument, NULL, OPT_RECIPIENT}, #if defined(ENABLE_CRYPTO_REFRESH) {"v3-pkesk-only", optional_argument, NULL, OPT_V3_PKESK_ONLY}, + {"enable-v6-skesk", optional_argument, NULL, OPT_ENABLE_V6_SKESK}, #endif {"home", required_argument, NULL, OPT_HOMEDIR}, {"homedir", required_argument, NULL, OPT_HOMEDIR}, @@ -414,6 +419,9 @@ setoption(rnp_cfg &cfg, int val, const char *arg) case OPT_V3_PKESK_ONLY: cfg.set_bool(CFG_V3_PKESK_ONLY, true); return true; + case OPT_ENABLE_V6_SKESK: + cfg.set_bool(CFG_ENABLE_V6_SKESK, true); + return true; #endif case OPT_ARMOR: cfg.set_bool(CFG_ARMOR, true); diff --git a/src/rnp/rnpcfg.h b/src/rnp/rnpcfg.h index eda6960efe..ebcc64a85f 100644 --- a/src/rnp/rnpcfg.h +++ b/src/rnp/rnpcfg.h @@ -51,7 +51,8 @@ #define CFG_USERID "userid" /* userid for the ongoing operation */ #define CFG_RECIPIENTS "recipients" /* list of encrypted data recipients */ #if defined(ENABLE_CRYPTO_REFRESH) -#define CFG_V3_PKESK_ONLY "v3-pkesk-only" /* disable v6 PKESK */ +#define CFG_V3_PKESK_ONLY "v3-pkesk-only" /* disable v6 PKESK */ +#define CFG_ENABLE_V6_SKESK "enable-v6-skesk" /* disable v6 SKESK */ #endif #define CFG_SIGNERS "signers" /* list of signers */ #define CFG_HOMEDIR "homedir" /* home directory - folder with keyrings and so on */ diff --git a/src/tests/data/test_v6_valid_data/transferable_pubkey_v6.asc b/src/tests/data/test_crypto_refresh/transferable_pubkey_v6.asc similarity index 100% rename from src/tests/data/test_v6_valid_data/transferable_pubkey_v6.asc rename to src/tests/data/test_crypto_refresh/transferable_pubkey_v6.asc diff --git a/src/tests/data/test_v6_valid_data/transferable_seckey_v6.asc b/src/tests/data/test_crypto_refresh/transferable_seckey_v6.asc similarity index 100% rename from src/tests/data/test_v6_valid_data/transferable_seckey_v6.asc rename to src/tests/data/test_crypto_refresh/transferable_seckey_v6.asc diff --git a/src/tests/data/test_v6_valid_data/v6pkesk.asc b/src/tests/data/test_crypto_refresh/v6_pkesk_x25519_aead_ocb.asc similarity index 100% rename from src/tests/data/test_v6_valid_data/v6pkesk.asc rename to src/tests/data/test_crypto_refresh/v6_pkesk_x25519_aead_ocb.asc diff --git a/src/tests/data/test_crypto_refresh/v6_skesk_eax.asc b/src/tests/data/test_crypto_refresh/v6_skesk_eax.asc new file mode 100644 index 0000000000..fdb5b18ecc --- /dev/null +++ b/src/tests/data/test_crypto_refresh/v6_skesk_eax.asc @@ -0,0 +1,7 @@ +-----BEGIN PGP MESSAGE----- + +w0AGHgcBCwMIpa5XnR/F2Cv/aSJPkZmTs1Bvo7WaanPP+MXvxfQcV/tU4cImgV14 +KPX5LEVOtl6+AKtZhsaObnxV0mkCBwEGn/kOOzIZZPOkKRPI3MZhkyUBUifvt+rq +pJ8EwuZ0F11KPSJu1q/LnKmsEiwUcOEcY9TAqyQcapOK1Iv5mlqZuQu6gyXeYQR1 +QCWKt5Wala0FHdqW6xVDHf719eIlXKeCYVRuM5o= +-----END PGP MESSAGE----- diff --git a/src/tests/data/test_crypto_refresh/v6_skesk_ocb.asc b/src/tests/data/test_crypto_refresh/v6_skesk_ocb.asc new file mode 100644 index 0000000000..f11e5cf8f2 --- /dev/null +++ b/src/tests/data/test_crypto_refresh/v6_skesk_ocb.asc @@ -0,0 +1,7 @@ +-----BEGIN PGP MESSAGE----- + +wz8GHQcCCwMIVqKY0vXjZFP/z8xcEWZO2520JZDX3EawckG2EsOBLP/76gDyNHsl +ZBEj+IeuYNT9YU4IN9gZ02zSaQIHAgYgpmH3MfyaMDK1YjMmAn46XY21dI6+/wsM +WRDQns3WQf+f04VidYA1vEl1TOG/P/+n2tCjuBBPUTPPQqQQCoPu9MobSAGohGv0 +K82nyM6dZeIS8wHLzZj9yt5pSod61CRzI/boVw== +-----END PGP MESSAGE----- diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp index 73709dd352..9a786fa60c 100644 --- a/src/tests/ffi-enc.cpp +++ b/src/tests/ffi-enc.cpp @@ -829,6 +829,91 @@ TEST_F(rnp_tests, test_ffi_decrypt_pk_unlocked) } #if defined(ENABLE_CRYPTO_REFRESH) +TEST_F(rnp_tests, test_ffi_decrypt_v6_skesk_test_vectors) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_verify_t verify; + + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_crypto_refresh/v6_skesk_eax.asc")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_string_equal(file_to_str("decrypted").c_str(), "Hello, world!"); + assert_int_equal(unlink("decrypted"), 0); + rnp_input_destroy(input); + rnp_output_destroy(output); + rnp_op_verify_destroy(verify); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_crypto_refresh/v6_skesk_ocb.asc")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_string_equal(file_to_str("decrypted").c_str(), "Hello, world!"); + assert_int_equal(unlink("decrypted"), 0); + rnp_input_destroy(input); + rnp_output_destroy(output); + rnp_op_verify_destroy(verify); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_v6_skesk_enc_dec) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_encrypt_t enc = NULL; + rnp_op_verify_t verify; + + const char plaintext[] = "Test Plaintext String"; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + std::vector ciphers = {"AES128", "AES192", "AES256"}; + std::vector aead_modes = {"EAX", "OCB"}; + + for (auto aead : aead_modes) + for (auto cipher : ciphers) { + assert_rnp_success(rnp_output_to_path(&output, "encrypted")); + assert_rnp_success(rnp_input_from_memory( + &input, (const uint8_t *) plaintext, strlen(plaintext), false)); + assert_rnp_success(rnp_op_encrypt_create(&enc, ffi, input, output)); + assert_rnp_success( + rnp_op_encrypt_add_password(enc, "password", NULL, 0, "AES256")); + assert_rnp_success(rnp_op_encrypt_set_cipher(enc, cipher.c_str())); + assert_rnp_success(rnp_op_encrypt_set_aead(enc, aead.c_str())); + assert_rnp_success(rnp_op_encrypt_enable_skesk_v6(enc)); + assert_rnp_success(rnp_op_encrypt_execute(enc)); + rnp_input_destroy(input); + rnp_output_destroy(output); + rnp_op_encrypt_destroy(enc); + + assert_rnp_success(rnp_ffi_set_pass_provider( + ffi, ffi_string_password_provider, (void *) "password")); + + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + rnp_input_destroy(input); + rnp_output_destroy(output); + rnp_op_verify_destroy(verify); + + assert_int_equal(unlink("decrypted"), 0); + assert_int_equal(unlink("encrypted"), 0); + } + + rnp_ffi_destroy(ffi); +} + TEST_F(rnp_tests, test_ffi_decrypt_v6_pkesk_test_vector) { rnp_ffi_t ffi = NULL; @@ -836,9 +921,10 @@ TEST_F(rnp_tests, test_ffi_decrypt_v6_pkesk_test_vector) rnp_output_t output = NULL; assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_true(import_all_keys(ffi, "data/test_v6_valid_data/transferable_seckey_v6.asc")); + assert_true(import_all_keys(ffi, "data/test_crypto_refresh/transferable_seckey_v6.asc")); - assert_rnp_success(rnp_input_from_path(&input, "data/test_v6_valid_data/v6pkesk.asc")); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_crypto_refresh/v6_pkesk_x25519_aead_ocb.asc")); assert_non_null(input); assert_rnp_success(rnp_output_to_null(&output)); assert_rnp_success(rnp_decrypt(ffi, input, output)); @@ -990,7 +1076,7 @@ TEST_F(rnp_tests, test_ffi_encrypt_pk_with_v6_key) // setup FFI assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); - assert_true(import_all_keys(ffi, "data/test_v6_valid_data/transferable_seckey_v6.asc")); + assert_true(import_all_keys(ffi, "data/test_crypto_refresh/transferable_seckey_v6.asc")); std::vector ciphers = {"AES128", "AES192", "AES256"}; std::vector aead_modes = {"None", "EAX", "OCB"}; diff --git a/src/tests/ffi-key.cpp b/src/tests/ffi-key.cpp index 9a71cb0bb9..8a15652d3b 100644 --- a/src/tests/ffi-key.cpp +++ b/src/tests/ffi-key.cpp @@ -3212,7 +3212,7 @@ TEST_F(rnp_tests, test_ffi_v6_cert_import) assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); assert_rnp_success( - rnp_input_from_path(&input, "data/test_v6_valid_data/transferable_pubkey_v6.asc")); + rnp_input_from_path(&input, "data/test_crypto_refresh/transferable_pubkey_v6.asc")); assert_rnp_success( rnp_import_keys(ffi, input, @@ -3299,7 +3299,7 @@ TEST_F(rnp_tests, test_ffi_v6_seckey_import) assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); assert_rnp_success( - rnp_input_from_path(&input, "data/test_v6_valid_data/transferable_seckey_v6.asc")); + rnp_input_from_path(&input, "data/test_crypto_refresh/transferable_seckey_v6.asc")); assert_rnp_success( rnp_import_keys(ffi, input,