From 4e365aab7da3b5a94d77a2569d4b6d7dfe8d8107 Mon Sep 17 00:00:00 2001 From: Johannes Roth Date: Tue, 26 Mar 2024 16:41:17 +0100 Subject: [PATCH] add v6 SKESK --- include/repgp/repgp_def.h | 8 +- include/rnp/rnp.h | 11 ++ src/lib/rnp.cpp | 14 +- src/librepgp/stream-ctx.h | 6 +- src/librepgp/stream-packet.cpp | 126 +++++++++++++++++- src/librepgp/stream-packet.h | 5 + src/librepgp/stream-parse.cpp | 45 +++++-- src/librepgp/stream-write.cpp | 40 +++++- src/rnp/fficli.cpp | 6 +- src/rnp/rnp.cpp | 8 ++ src/rnp/rnpcfg.h | 3 +- .../transferable_pubkey_v6.asc | 0 .../transferable_seckey_v6.asc | 0 .../v6_pkesk_x25519_aead_ocb.asc} | 0 .../data/test_crypto_refresh/v6_skesk_eax | 8 ++ .../data/test_crypto_refresh/v6_skesk_eax.asc | 7 + .../test_crypto_refresh/v6_skesk_eax.asc.pgp | 0 .../data/test_crypto_refresh/v6_skesk_ocb.asc | 7 + src/tests/ffi-enc.cpp | 89 ++++++++++++- src/tests/ffi-key.cpp | 4 +- 20 files changed, 362 insertions(+), 25 deletions(-) rename src/tests/data/{test_v6_valid_data => test_crypto_refresh}/transferable_pubkey_v6.asc (100%) rename src/tests/data/{test_v6_valid_data => test_crypto_refresh}/transferable_seckey_v6.asc (100%) rename src/tests/data/{test_v6_valid_data/v6pkesk.asc => test_crypto_refresh/v6_pkesk_x25519_aead_ocb.asc} (100%) create mode 100644 src/tests/data/test_crypto_refresh/v6_skesk_eax create mode 100644 src/tests/data/test_crypto_refresh/v6_skesk_eax.asc create mode 100644 src/tests/data/test_crypto_refresh/v6_skesk_eax.asc.pgp create mode 100644 src/tests/data/test_crypto_refresh/v6_skesk_ocb.asc diff --git a/include/repgp/repgp_def.h b/include/repgp/repgp_def.h index 88f06d554..7976beb8a 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 55b06d074..26c1104e6 100644 --- a/include/rnp/rnp.h +++ b/include/rnp/rnp.h @@ -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 /** diff --git a/src/lib/rnp.cpp b/src/lib/rnp.cpp index 7e1a1965d..ea626a044 100644 --- a/src/lib/rnp.cpp +++ b/src/lib/rnp.cpp @@ -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 @@ -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", diff --git a/src/librepgp/stream-ctx.h b/src/librepgp/stream-ctx.h index 12639bc4e..b59221690 100644 --- a/src/librepgp/stream-ctx.h +++ b/src/librepgp/stream-ctx.h @@ -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 @@ -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 recipients{}; /* recipients of the encrypted message */ std::list passwords{}; /* passwords to encrypt message */ diff --git a/src/librepgp/stream-packet.cpp b/src/librepgp/stream-packet.cpp index f07530329..069f43277 100644 --- a/src/librepgp/stream-packet.cpp +++ b/src/librepgp/stream-packet.cpp @@ -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); @@ -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 */ @@ -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) { @@ -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; } @@ -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; } diff --git a/src/librepgp/stream-packet.h b/src/librepgp/stream-packet.h index 8090e2cd9..e0dc3c094 100644 --- a/src/librepgp/stream-packet.h +++ b/src/librepgp/stream-packet.h @@ -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 */ diff --git a/src/librepgp/stream-parse.cpp b/src/librepgp/stream-parse.cpp index 60e6b528f..bf9bf38d5 100644 --- a/src/librepgp/stream-parse.cpp +++ b/src/librepgp/stream-parse.cpp @@ -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 @@ -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 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; } @@ -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; diff --git a/src/librepgp/stream-write.cpp b/src/librepgp/stream-write.cpp index 098f8d6d1..bf74570b1 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 @@ -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 */ @@ -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 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) { @@ -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; diff --git a/src/rnp/fficli.cpp b/src/rnp/fficli.cpp index bf3de77cb..d247894cd 100644 --- a/src/rnp/fficli.cpp +++ b/src/rnp/fficli.cpp @@ -2729,10 +2729,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 a4b41f1ec..c5db06f3b 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" @@ -131,6 +134,7 @@ enum optdefs { OPT_RECIPIENT, #if defined(ENABLE_CRYPTO_REFRESH) OPT_V3_PKESK_ONLY, + OPT_ENABLE_V6_SKESK, #endif OPT_ARMOR, OPT_HOMEDIR, @@ -198,6 +202,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}, @@ -411,6 +416,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 585ca174e..8f2c6ac5d 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 b/src/tests/data/test_crypto_refresh/v6_skesk_eax new file mode 100644 index 000000000..51e43e802 --- /dev/null +++ b/src/tests/data/test_crypto_refresh/v6_skesk_eax @@ -0,0 +1,8 @@ +-----BEGIN PGP MESSAGE----- + +wV0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmHzxjV8bU/gXzO +WgBM85PMiVi93AZfJfhK9QmxfdNnZBjeo1VDeVZheQHgaVf7yopqR6W1FT6NOrfS +aQIHAgZhZBZTW+CwcW1g4FKlbExAf56zaw76/prQoN+bAzxpohup69LA7JW/Vp0l +yZnuSj3hcFj0DfqLTGgr4/u717J+sPWbtQBfgMfG9AOIwwrUBqsFE9zW+f1zdlYo +bhF30A+IitsxxA== +-----END PGP MESSAGE----- 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 000000000..fdb5b18ec --- /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_eax.asc.pgp b/src/tests/data/test_crypto_refresh/v6_skesk_eax.asc.pgp new file mode 100644 index 000000000..e69de29bb 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 000000000..f11e5cf8f --- /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 9512344bb..a35fcac25 100644 --- a/src/tests/ffi-enc.cpp +++ b/src/tests/ffi-enc.cpp @@ -737,6 +737,88 @@ 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_null(&output)); + 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); + + 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_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); + + 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); + + 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; @@ -744,9 +826,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)); @@ -768,7 +851,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 d47d798b3..9c84f3cec 100644 --- a/src/tests/ffi-key.cpp +++ b/src/tests/ffi-key.cpp @@ -3193,7 +3193,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, @@ -3239,7 +3239,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,