/* * Copyright (c) 2018-2020, 2023 [Ribose Inc](https://www.ribose.com). * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include #include #include #include #ifdef HAVE_UNISTD_H #include #else #include "uniwin.h" #endif #include #include "time-utils.h" #include "stream-def.h" #include "stream-dump.h" #include "stream-armor.h" #include "stream-packet.h" #include "stream-parse.h" #include "types.h" #include "ctype.h" #include "crypto/symmetric.h" #include "crypto/s2k.h" #include "fingerprint.hpp" #include "key.hpp" #include "json-utils.h" #include #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include static const id_str_pair packet_tag_map[] = { {PGP_PKT_RESERVED, "Reserved"}, {PGP_PKT_PK_SESSION_KEY, "Public-Key Encrypted Session Key"}, {PGP_PKT_SIGNATURE, "Signature"}, {PGP_PKT_SK_SESSION_KEY, "Symmetric-Key Encrypted Session Key"}, {PGP_PKT_ONE_PASS_SIG, "One-Pass Signature"}, {PGP_PKT_SECRET_KEY, "Secret Key"}, {PGP_PKT_PUBLIC_KEY, "Public Key"}, {PGP_PKT_SECRET_SUBKEY, "Secret Subkey"}, {PGP_PKT_COMPRESSED, "Compressed Data"}, {PGP_PKT_SE_DATA, "Symmetrically Encrypted Data"}, {PGP_PKT_MARKER, "Marker"}, {PGP_PKT_LITDATA, "Literal Data"}, {PGP_PKT_TRUST, "Trust"}, {PGP_PKT_USER_ID, "User ID"}, {PGP_PKT_PUBLIC_SUBKEY, "Public Subkey"}, {PGP_PKT_RESERVED2, "reserved2"}, {PGP_PKT_RESERVED3, "reserved3"}, {PGP_PKT_USER_ATTR, "User Attribute"}, {PGP_PKT_SE_IP_DATA, "Symmetric Encrypted and Integrity Protected Data"}, {PGP_PKT_MDC, "Modification Detection Code"}, {PGP_PKT_AEAD_ENCRYPTED, "AEAD Encrypted Data Packet"}, {0x00, NULL}, }; static const id_str_pair sig_type_map[] = { {PGP_SIG_BINARY, "Signature of a binary document"}, {PGP_SIG_TEXT, "Signature of a canonical text document"}, {PGP_SIG_STANDALONE, "Standalone signature"}, {PGP_CERT_GENERIC, "Generic User ID certification"}, {PGP_CERT_PERSONA, "Personal User ID certification"}, {PGP_CERT_CASUAL, "Casual User ID certification"}, {PGP_CERT_POSITIVE, "Positive User ID certification"}, {PGP_SIG_SUBKEY, "Subkey Binding Signature"}, {PGP_SIG_PRIMARY, "Primary Key Binding Signature"}, {PGP_SIG_DIRECT, "Direct-key signature"}, {PGP_SIG_REV_KEY, "Key revocation signature"}, {PGP_SIG_REV_SUBKEY, "Subkey revocation signature"}, {PGP_SIG_REV_CERT, "Certification revocation signature"}, {PGP_SIG_TIMESTAMP, "Timestamp signature"}, {PGP_SIG_3RD_PARTY, "Third-Party Confirmation signature"}, {0x00, NULL}, }; static const id_str_pair sig_subpkt_type_map[] = { {PGP_SIG_SUBPKT_CREATION_TIME, "signature creation time"}, {PGP_SIG_SUBPKT_EXPIRATION_TIME, "signature expiration time"}, {PGP_SIG_SUBPKT_EXPORT_CERT, "exportable certification"}, {PGP_SIG_SUBPKT_TRUST, "trust signature"}, {PGP_SIG_SUBPKT_REGEXP, "regular expression"}, {PGP_SIG_SUBPKT_REVOCABLE, "revocable"}, {PGP_SIG_SUBPKT_KEY_EXPIRY, "key expiration time"}, {PGP_SIG_SUBPKT_PREFERRED_SKA, "preferred symmetric algorithms"}, {PGP_SIG_SUBPKT_REVOCATION_KEY, "revocation key"}, {PGP_SIG_SUBPKT_ISSUER_KEY_ID, "issuer key ID"}, {PGP_SIG_SUBPKT_NOTATION_DATA, "notation data"}, {PGP_SIG_SUBPKT_PREFERRED_HASH, "preferred hash algorithms"}, {PGP_SIG_SUBPKT_PREF_COMPRESS, "preferred compression algorithms"}, {PGP_SIG_SUBPKT_KEYSERV_PREFS, "key server preferences"}, {PGP_SIG_SUBPKT_PREF_KEYSERV, "preferred key server"}, {PGP_SIG_SUBPKT_PRIMARY_USER_ID, "primary user ID"}, {PGP_SIG_SUBPKT_POLICY_URI, "policy URI"}, {PGP_SIG_SUBPKT_KEY_FLAGS, "key flags"}, {PGP_SIG_SUBPKT_SIGNERS_USER_ID, "signer's user ID"}, {PGP_SIG_SUBPKT_REVOCATION_REASON, "reason for revocation"}, {PGP_SIG_SUBPKT_FEATURES, "features"}, {PGP_SIG_SUBPKT_SIGNATURE_TARGET, "signature target"}, {PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE, "embedded signature"}, {PGP_SIG_SUBPKT_ISSUER_FPR, "issuer fingerprint"}, {PGP_SIG_SUBPKT_PREFERRED_AEAD, "preferred AEAD algorithms"}, {0x00, NULL}, }; static const id_str_pair key_type_map[] = { {PGP_PKT_SECRET_KEY, "Secret key"}, {PGP_PKT_PUBLIC_KEY, "Public key"}, {PGP_PKT_SECRET_SUBKEY, "Secret subkey"}, {PGP_PKT_PUBLIC_SUBKEY, "Public subkey"}, {0x00, NULL}, }; static const id_str_pair pubkey_alg_map[] = { {PGP_PKA_RSA, "RSA (Encrypt or Sign)"}, {PGP_PKA_RSA_ENCRYPT_ONLY, "RSA (Encrypt-Only)"}, {PGP_PKA_RSA_SIGN_ONLY, "RSA (Sign-Only)"}, {PGP_PKA_ELGAMAL, "Elgamal (Encrypt-Only)"}, {PGP_PKA_DSA, "DSA"}, {PGP_PKA_ECDH, "ECDH"}, {PGP_PKA_ECDSA, "ECDSA"}, {PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN, "Elgamal"}, {PGP_PKA_RESERVED_DH, "Reserved for DH (X9.42)"}, {PGP_PKA_EDDSA, "EdDSA"}, {PGP_PKA_SM2, "SM2"}, #if defined(ENABLE_CRYPTO_REFRESH) {PGP_PKA_ED25519, "Ed25519"}, {PGP_PKA_X25519, "X25519"}, {PGP_PKA_ED448, "Ed448"}, {PGP_PKA_X448, "X448"}, #endif #if defined(ENABLE_PQC) {PGP_PKA_KYBER768_X25519, "ML-KEM-768 + X25519"}, #endif #if defined(ENABLE_PQC) && defined(ENABLE_CRYPTO_REFRESH) {PGP_PKA_KYBER1024_X448, "ML-KEM-1024 + X448"}, {PGP_PKA_KYBER768_P384, "ML-KEM-768 + NIST P-256"}, {PGP_PKA_KYBER1024_P521, "ML-KEM-1024 + NIST P-384"}, {PGP_PKA_KYBER768_BP384, "ML-KEM-768 + Brainpool256"}, {PGP_PKA_KYBER1024_BP512, "ML-KEM-1024 + Brainpool384"}, {PGP_PKA_DILITHIUM3_ED25519, "ML-DSA-65 + ED25519"}, {PGP_PKA_DILITHIUM5_ED448, "ML-DSA-87 + X448"}, {PGP_PKA_DILITHIUM3_P384, "ML-DSA-65 + NIST P-256"}, {PGP_PKA_DILITHIUM5_P521, "ML-DSA-87 + NIST P-384"}, {PGP_PKA_DILITHIUM3_BP384, "ML-DSA-65 + Brainpool256"}, {PGP_PKA_DILITHIUM5_BP512, "ML-DSA-87 + Brainpool384"}, {PGP_PKA_SPHINCSPLUS_SHAKE_128f, "SLH-DSA-SHAKE-128f"}, {PGP_PKA_SPHINCSPLUS_SHAKE_128s, "SLH-DSA-SHAKE-128s"}, {PGP_PKA_SPHINCSPLUS_SHAKE_256s, "SLH-DSA-SHAKE-256s"}, #endif {0x00, NULL}, }; static const id_str_pair symm_alg_map[] = { {PGP_SA_PLAINTEXT, "Plaintext"}, {PGP_SA_IDEA, "IDEA"}, {PGP_SA_TRIPLEDES, "TripleDES"}, {PGP_SA_CAST5, "CAST5"}, {PGP_SA_BLOWFISH, "Blowfish"}, {PGP_SA_AES_128, "AES-128"}, {PGP_SA_AES_192, "AES-192"}, {PGP_SA_AES_256, "AES-256"}, {PGP_SA_TWOFISH, "Twofish"}, {PGP_SA_CAMELLIA_128, "Camellia-128"}, {PGP_SA_CAMELLIA_192, "Camellia-192"}, {PGP_SA_CAMELLIA_256, "Camellia-256"}, {PGP_SA_SM4, "SM4"}, {0x00, NULL}, }; static const id_str_pair hash_alg_map[] = { {PGP_HASH_MD5, "MD5"}, {PGP_HASH_SHA1, "SHA1"}, {PGP_HASH_RIPEMD, "RIPEMD160"}, {PGP_HASH_SHA256, "SHA256"}, {PGP_HASH_SHA384, "SHA384"}, {PGP_HASH_SHA512, "SHA512"}, {PGP_HASH_SHA224, "SHA224"}, {PGP_HASH_SM3, "SM3"}, {PGP_HASH_SHA3_256, "SHA3-256"}, {PGP_HASH_SHA3_512, "SHA3-512"}, {0x00, NULL}, }; static const id_str_pair z_alg_map[] = { {PGP_C_NONE, "Uncompressed"}, {PGP_C_ZIP, "ZIP"}, {PGP_C_ZLIB, "ZLib"}, {PGP_C_BZIP2, "BZip2"}, {0x00, NULL}, }; static const id_str_pair aead_alg_map[] = { {PGP_AEAD_NONE, "None"}, {PGP_AEAD_EAX, "EAX"}, {PGP_AEAD_OCB, "OCB"}, {0x00, NULL}, }; static const id_str_pair revoc_reason_map[] = { {PGP_REVOCATION_NO_REASON, "No reason"}, {PGP_REVOCATION_SUPERSEDED, "Superseded"}, {PGP_REVOCATION_COMPROMISED, "Compromised"}, {PGP_REVOCATION_RETIRED, "Retired"}, {PGP_REVOCATION_NO_LONGER_VALID, "No longer valid"}, {0x00, NULL}, }; typedef struct pgp_dest_indent_param_t { int level; bool lstart; pgp_dest_t *writedst; } pgp_dest_indent_param_t; static rnp_result_t indent_dst_write(pgp_dest_t *dst, const void *buf, size_t len) { pgp_dest_indent_param_t *param = (pgp_dest_indent_param_t *) dst->param; const char * line = (const char *) buf; char indent[4] = {' ', ' ', ' ', ' '}; if (!len) { return RNP_SUCCESS; } do { if (param->lstart) { for (int i = 0; i < param->level; i++) { dst_write(param->writedst, indent, sizeof(indent)); } param->lstart = false; } for (size_t i = 0; i < len; i++) { if ((line[i] == '\n') || (i == len - 1)) { dst_write(param->writedst, line, i + 1); param->lstart = line[i] == '\n'; line += i + 1; len -= i + 1; break; } } } while (len > 0); return RNP_SUCCESS; } static void indent_dst_close(pgp_dest_t *dst, bool discard) { free(dst->param); } static rnp_result_t init_indent_dest(pgp_dest_t &dst, pgp_dest_t *origdst) { pgp_dest_indent_param_t *param; if (!init_dst_common(&dst, sizeof(*param))) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } dst.write = indent_dst_write; dst.close = indent_dst_close; dst.finish = NULL; dst.no_cache = true; param = (pgp_dest_indent_param_t *) dst.param; param->writedst = origdst; param->lstart = true; param->level = 0; return RNP_SUCCESS; } static void indent_dest_increase(pgp_dest_t &dst) { ((pgp_dest_indent_param_t *) dst.param)->level++; } static void indent_dest_decrease(pgp_dest_t &dst) { pgp_dest_indent_param_t *param = (pgp_dest_indent_param_t *) dst.param; if (param->level > 0) { param->level--; } } static size_t vsnprinthex(char *str, size_t slen, const uint8_t *buf, size_t buflen) { static const char *hexes = "0123456789abcdef"; size_t idx = 0; for (size_t i = 0; (i < buflen) && (i < (slen - 1) / 2); i++) { str[idx++] = hexes[buf[i] >> 4]; str[idx++] = hexes[buf[i] & 0xf]; } str[idx] = '\0'; return buflen * 2; } static void dst_print_mpi(pgp_dest_t &dst, const char *name, const pgp::mpi &mpi, bool dumpbin) { if (!dumpbin) { dst_printf(dst, "%s: %zu bits\n", name, mpi.bits()); } else { char hex[5000]; vsnprinthex(hex, sizeof(hex), mpi.data(), mpi.size()); dst_printf(dst, "%s: %zu bits, %s\n", name, mpi.bits(), hex); } } #if defined(ENABLE_CRYPTO_REFRESH) || defined(ENABLE_PQC) static void dst_print_vec(pgp_dest_t & dst, const char * name, std::vector const &data, bool dumpbin) { if (!dumpbin) { dst_printf(dst, "%s\n", name); } else { std::vector hex(2 * data.size()); vsnprinthex(hex.data(), hex.size(), data.data(), data.size()); dst_printf(dst, "%s, %s\n", name, hex.data()); } } #endif static void dst_print_palg(pgp_dest_t &dst, const char *name, pgp_pubkey_alg_t palg) { const char *palg_name = id_str_pair::lookup(pubkey_alg_map, palg, "Unknown"); if (!name) { name = "public key algorithm"; } dst_printf(dst, "%s: %d (%s)\n", name, (int) palg, palg_name); } static void dst_print_halg(pgp_dest_t &dst, const char *name, pgp_hash_alg_t halg) { const char *halg_name = id_str_pair::lookup(hash_alg_map, halg, "Unknown"); if (!name) { name = "hash algorithm"; } dst_printf(dst, "%s: %d (%s)\n", name, (int) halg, halg_name); } static void dst_print_salg(pgp_dest_t &dst, const char *name, pgp_symm_alg_t salg) { const char *salg_name = id_str_pair::lookup(symm_alg_map, salg, "Unknown"); if (!name) { name = "symmetric algorithm"; } dst_printf(dst, "%s: %d (%s)\n", name, (int) salg, salg_name); } static void dst_print_aalg(pgp_dest_t &dst, const char *name, pgp_aead_alg_t aalg) { const char *aalg_name = id_str_pair::lookup(aead_alg_map, aalg, "Unknown"); if (!name) { name = "aead algorithm"; } dst_printf(dst, "%s: %d (%s)\n", name, (int) aalg, aalg_name); } static void dst_print_zalg(pgp_dest_t &dst, const char *name, pgp_compression_type_t zalg) { const char *zalg_name = id_str_pair::lookup(z_alg_map, zalg, "Unknown"); if (!name) { name = "compression algorithm"; } dst_printf(dst, "%s: %d (%s)\n", name, (int) zalg, zalg_name); } static void dst_print_str(pgp_dest_t &dst, const char *name, const std::string &str) { dst_printf(dst, "%s: ", name); dst_write(&dst, str.data(), str.size()); dst_printf(dst, "\n"); } static void dst_print_algs(pgp_dest_t & dst, const std::string & name, const std::vector &algs, const id_str_pair map[]) { dst_printf(dst, "%s: ", name.c_str()); for (size_t i = 0; i < algs.size(); i++) { auto comma = i + 1 < algs.size() ? ", " : ""; dst_printf(dst, "%s%s", id_str_pair::lookup(map, algs[i], "Unknown"), comma); } dst_printf(dst, " ("); for (size_t i = 0; i < algs.size(); i++) { auto comma = i + 1 < algs.size() ? ", " : ""; dst_printf(dst, "%" PRIu8 "%s", algs[i], comma); } dst_printf(dst, ")\n"); } static void dst_print_sig_type(pgp_dest_t &dst, const char *name, pgp_sig_type_t sigtype) { const char *sig_name = id_str_pair::lookup(sig_type_map, sigtype, "Unknown"); if (!name) { name = "signature type"; } dst_printf(dst, "%s: %d (%s)\n", name, (int) sigtype, sig_name); } static void dst_print_hex( pgp_dest_t &dst, const std::string &name, const uint8_t *data, size_t len, bool bytes) { char hex[512]; vsnprinthex(hex, sizeof(hex), data, len); if (bytes) { dst_printf(dst, "%s: 0x%s (%d bytes)\n", name.c_str(), hex, (int) len); } else { dst_printf(dst, "%s: 0x%s\n", name.c_str(), hex); } } static void dst_print_keyid(pgp_dest_t &dst, const std::string &name, const pgp::KeyID &keyid) { dst_print_hex(dst, name, keyid.data(), keyid.size(), false); } static void dst_print_fp(pgp_dest_t & dst, const std::string & name, const pgp::Fingerprint &fp, bool size = true) { dst_print_hex(dst, name, fp.data(), fp.size(), size); } static void dst_print_s2k(pgp_dest_t &dst, pgp_s2k_t &s2k) { dst_printf(dst, "s2k specifier: %d\n", (int) s2k.specifier); if ((s2k.specifier == PGP_S2KS_EXPERIMENTAL) && s2k.gpg_ext_num) { dst_printf(dst, "GPG extension num: %d\n", (int) s2k.gpg_ext_num); if (s2k.gpg_ext_num == PGP_S2K_GPG_SMARTCARD) { static_assert(sizeof(s2k.gpg_serial) == 16, "invalid s2k->gpg_serial size"); size_t slen = s2k.gpg_serial_len > 16 ? 16 : s2k.gpg_serial_len; dst_print_hex(dst, "card serial number", s2k.gpg_serial, slen, true); } return; } if (s2k.specifier == PGP_S2KS_EXPERIMENTAL) { dst_print_hex(dst, "Unknown experimental s2k", s2k.experimental.data(), s2k.experimental.size(), true); return; } #if defined(ENABLE_CRYPTO_REFRESH) if (s2k.specifier == PGP_S2KS_ARGON2) { dst_print_hex(dst, "s2k salt", s2k.salt, s2k.salt_size(s2k.specifier), false); dst_printf(dst, "argon2 t: %d\n", s2k.argon2_t); dst_printf(dst, "argon2 p: %d\n", s2k.argon2_p); dst_printf(dst, "argon2 encoded_m: %d\n", s2k.argon2_encoded_m); } else #endif { dst_print_halg(dst, "s2k hash algorithm", s2k.hash_alg); if ((s2k.specifier == PGP_S2KS_SALTED) || (s2k.specifier == PGP_S2KS_ITERATED_AND_SALTED)) { dst_print_hex(dst, "s2k salt", s2k.salt, PGP_SALT_SIZE, false); } if (s2k.specifier == PGP_S2KS_ITERATED_AND_SALTED) { size_t real_iter = pgp_s2k_decode_iterations(s2k.iterations); dst_printf( dst, "s2k iterations: %zu (encoded as %u)\n", real_iter, s2k.iterations); } } } static void dst_print_time(pgp_dest_t &dst, const char *name, uint32_t time) { auto str = rnp_ctime(time).substr(0, 24); dst_printf(dst, "%s: %zu (%s%s)\n", name, (size_t) time, rnp_y2k38_warning(time) ? ">=" : "", str.c_str()); } static void dst_print_expiration(pgp_dest_t &dst, const char *name, uint32_t seconds) { if (seconds) { int days = seconds / (24 * 60 * 60); dst_printf(dst, "%s: %" PRIu32 " seconds (%d days)\n", name, seconds, days); } else { dst_printf(dst, "%s: 0 (never)\n", name); } } #define LINELEN 16 static void dst_hexdump(pgp_dest_t &dst, const uint8_t *src, size_t length) { size_t i; char line[LINELEN + 1]; for (i = 0; i < length; i++) { if (i % LINELEN == 0) { dst_printf(dst, "%.5zu | ", i); } dst_printf(dst, "%.02x ", (uint8_t) src[i]); line[i % LINELEN] = (isprint(src[i])) ? src[i] : '.'; if (i % LINELEN == LINELEN - 1) { line[LINELEN] = 0x0; dst_printf(dst, " | %s\n", line); } } if (i % LINELEN != 0) { for (; i % LINELEN != 0; i++) { dst_printf(dst, " "); line[i % LINELEN] = ' '; } line[LINELEN] = 0x0; dst_printf(dst, " | %s\n", line); } } static void dst_hexdump(pgp_dest_t &dst, const std::vector &data) { dst_hexdump(dst, data.data(), data.size()); } namespace rnp { using namespace pgp; /* Source wrapper which limits the number of bytes which may be read through it, * used on top of the decompressed packet contents during the dump. */ typedef struct dump_limited_src_param_t { pgp_source_t *readsrc; /* owned by the parent dump context, which outlives this source */ dump_budget_t *budget; } dump_limited_src_param_t; static bool dump_limited_src_read(pgp_source_t *src, void *buf, size_t len, size_t *readres) { auto param = static_cast(src->param); if (!param->budget->left) { if (!param->budget->hit) { RNP_LOG("too much decompressed data during the dump, stopping."); param->budget->hit = true; } return false; } len = std::min(len, param->budget->left); if (!param->readsrc->read(buf, len, readres)) { return false; } param->budget->left -= *readres; return true; } static void dump_limited_src_close(pgp_source_t *src) { free(src->param); } static rnp_result_t init_dump_limited_src(pgp_source_t *src, pgp_source_t *readsrc, dump_budget_t *budget) { if (!init_src_common(src, sizeof(dump_limited_src_param_t))) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } auto param = static_cast(src->param); param->readsrc = readsrc; param->budget = budget; src->raw_read = dump_limited_src_read; src->raw_close = dump_limited_src_close; src->type = PGP_STREAM_PARLEN_PACKET; return RNP_SUCCESS; } void DumpContext::copy_params(const DumpContext &ctx) { dump_mpi = ctx.dump_mpi; dump_packets = ctx.dump_packets; dump_grips = ctx.dump_grips; /* this could be called only from upper layer dumper */ layers = ctx.layers; stream_pkts = ctx.stream_pkts; failures = ctx.failures; dumped_pkts = ctx.dumped_pkts; zbudget = ctx.zbudget; } bool DumpContext::get_aead_hdr(pgp_aead_hdr_t &hdr) { uint8_t encpkt[64] = {0}; MemoryDest encdst(encpkt, sizeof(encpkt)); mem_dest_discard_overflow(&encdst.dst(), true); if (stream_read_packet(&src, &encdst.dst())) { return false; } size_t len = std::min(encdst.writeb(), sizeof(encpkt)); MemorySource memsrc(encpkt, len, false); return get_aead_src_hdr(&memsrc.src(), &hdr); } bool DumpContext::skip_cleartext() { char buf[4096]; size_t read = 0; size_t siglen = strlen(ST_SIG_BEGIN); char * hdrpos; while (!src.eof()) { if (!src.peek(buf, sizeof(buf) - 1, &read) || (read <= siglen)) { return false; } buf[read] = '\0'; if ((hdrpos = strstr(buf, ST_SIG_BEGIN))) { /* +1 here is to skip \n on the beginning of ST_SIG_BEGIN */ src.skip(hdrpos - buf + 1); return true; } src.skip(read - siglen + 1); } return false; } DumpContextDst::DumpContextDst(pgp_source_t &asrc, pgp_dest_t &adst) : DumpContext(asrc) { auto ret = init_indent_dest(dst, &adst); if (ret) { RNP_LOG("failed to init indent dest"); throw rnp_exception(RNP_ERROR_OUT_OF_MEMORY); } } DumpContextDst::~DumpContextDst() { if (dst.param) { dst_close(&dst, false); } } void DumpContextDst::dump_signature_subpacket(const pkt::sigsub::Raw &subpkt) { auto sname = id_str_pair::lookup(sig_subpkt_type_map, subpkt.raw_type(), "Unknown"); switch (subpkt.type()) { case pkt::sigsub::Type::CreationTime: { auto &sub = dynamic_cast(subpkt); dst_print_time(dst, sname, sub.time()); break; } case pkt::sigsub::Type::ExpirationTime: { auto &sub = dynamic_cast(subpkt); dst_print_expiration(dst, sname, sub.time()); break; } case pkt::sigsub::Type::ExportableCert: { auto &sub = dynamic_cast(subpkt); dst_printf(dst, "%s: %d\n", sname, sub.exportable()); break; } case pkt::sigsub::Type::Trust: { auto &sub = dynamic_cast(subpkt); dst_printf( dst, "%s: amount %" PRIu8 ", level %" PRIu8 "\n", sname, sub.amount(), sub.level()); break; } case pkt::sigsub::Type::RegExp: { auto &sub = dynamic_cast(subpkt); dst_print_str(dst, sname, sub.regexp()); break; } case pkt::sigsub::Type::Revocable: { auto &sub = dynamic_cast(subpkt); dst_printf(dst, "%s: %d\n", sname, sub.revocable()); break; } case pkt::sigsub::Type::KeyExpirationTime: { auto &sub = dynamic_cast(subpkt); dst_print_expiration(dst, sname, sub.time()); break; } case pkt::sigsub::Type::PreferredSymmetric: { auto &sub = dynamic_cast(subpkt); dst_print_algs(dst, "preferred symmetric algorithms", sub.algs(), symm_alg_map); break; } case pkt::sigsub::Type::RevocationKey: { auto &sub = dynamic_cast(subpkt); dst_printf(dst, "%s\n", sname); dst_printf(dst, "class: %" PRIu8 "\n", sub.rev_class()); dst_print_palg(dst, NULL, sub.alg()); dst_print_fp(dst, "fingerprint", sub.fp()); break; } case pkt::sigsub::Type::IssuerKeyID: { auto &sub = dynamic_cast(subpkt); dst_print_keyid(dst, sname, sub.keyid()); break; } case pkt::sigsub::Type::NotationData: { auto &sub = dynamic_cast(subpkt); if (sub.human_readable()) { dst_printf(dst, "%s: %s = ", sname, sub.name().c_str()); dst_printf( dst, "%.*s\n", (int) sub.value().size(), (const char *) sub.value().data()); } else { char hex[64]; vsnprinthex(hex, sizeof(hex), sub.value().data(), sub.value().size()); dst_printf(dst, "%s: %s = ", sname, sub.name().c_str()); dst_printf(dst, "0x%s (%zu bytes)\n", hex, sub.value().size()); } break; } case pkt::sigsub::Type::PreferredHash: { auto &sub = dynamic_cast(subpkt); dst_print_algs(dst, "preferred hash algorithms", sub.algs(), hash_alg_map); break; } case pkt::sigsub::Type::PreferredCompress: { auto &sub = dynamic_cast(subpkt); dst_print_algs(dst, "preferred compression algorithms", sub.algs(), z_alg_map); break; } case pkt::sigsub::Type::KeyserverPrefs: { auto &sub = dynamic_cast(subpkt); dst_printf(dst, "%s\n", sname); dst_printf(dst, "no-modify: %d\n", sub.no_modify()); break; } case pkt::sigsub::Type::PreferredKeyserver: { auto &sub = dynamic_cast(subpkt); dst_print_str(dst, sname, sub.keyserver()); break; } case pkt::sigsub::Type::PrimaryUserID: { auto &sub = dynamic_cast(subpkt); dst_printf(dst, "%s: %d\n", sname, sub.primary()); break; } case pkt::sigsub::Type::PolicyURI: { auto &sub = dynamic_cast(subpkt); dst_print_str(dst, sname, sub.URI()); break; } case pkt::sigsub::Type::KeyFlags: { auto & sub = dynamic_cast(subpkt); uint8_t flg = sub.flags(); dst_printf(dst, "%s: 0x%02x ( ", sname, flg); dst_printf(dst, "%s", flg ? "" : "none"); dst_printf(dst, "%s", flg & PGP_KF_CERTIFY ? "certify " : ""); dst_printf(dst, "%s", flg & PGP_KF_SIGN ? "sign " : ""); dst_printf(dst, "%s", flg & PGP_KF_ENCRYPT_COMMS ? "encrypt_comm " : ""); dst_printf(dst, "%s", flg & PGP_KF_ENCRYPT_STORAGE ? "encrypt_storage " : ""); dst_printf(dst, "%s", flg & PGP_KF_SPLIT ? "split " : ""); dst_printf(dst, "%s", flg & PGP_KF_AUTH ? "auth " : ""); dst_printf(dst, "%s", flg & PGP_KF_SHARED ? "shared " : ""); dst_printf(dst, ")\n"); break; } case pkt::sigsub::Type::SignersUserID: { auto &sub = dynamic_cast(subpkt); dst_print_str(dst, sname, sub.signer()); break; } case pkt::sigsub::Type::RevocationReason: { auto &sub = dynamic_cast(subpkt); auto reason = id_str_pair::lookup(revoc_reason_map, sub.code(), "Unknown"); dst_printf(dst, "%s: %" PRIu8 " (%s)\n", sname, sub.code(), reason); dst_print_str(dst, "message", sub.reason()); break; } case pkt::sigsub::Type::Features: { auto &sub = dynamic_cast(subpkt); dst_printf(dst, "%s: 0x%02x ( ", sname, sub.features()); dst_printf(dst, "%s", sub.features() & PGP_KEY_FEATURE_MDC ? "mdc " : ""); dst_printf(dst, "%s", sub.features() & PGP_KEY_FEATURE_AEAD ? "aead " : ""); dst_printf(dst, "%s", sub.features() & PGP_KEY_FEATURE_V5 ? "v5 keys " : ""); #if defined(ENABLE_CRYPTO_REFRESH) dst_printf(dst, "%s", sub.features() & PGP_KEY_FEATURE_SEIPDV2 ? "SEIPD v2 " : ""); #endif dst_printf(dst, ")\n"); break; } case pkt::sigsub::Type::EmbeddedSignature: { auto &sub = dynamic_cast(subpkt); dst_printf(dst, "%s:\n", sname); pkt::Signature sig(*sub.signature()); dump_signature_pkt(sig); break; } case pkt::sigsub::Type::IssuerFingerprint: { auto &sub = dynamic_cast(subpkt); dst_print_fp(dst, sname, sub.fp()); break; } case pkt::sigsub::Type::PreferredAEAD: { auto &sub = dynamic_cast(subpkt); dst_print_algs(dst, "preferred aead algorithms", sub.algs(), aead_alg_map); break; } default: if (!dump_packets) { indent_dest_increase(dst); dst_hexdump(dst, subpkt.data()); indent_dest_decrease(dst); } } } void DumpContextDst::dump_signature_subpackets(const pkt::Signature &sig, bool hashed) { bool empty = true; for (auto &subpkt : sig.subpkts) { if (subpkt->hashed() != hashed) { continue; } empty = false; dst_printf( dst, ":type %" PRIu8 ", len %zu", subpkt->raw_type(), subpkt->data().size()); dst_printf(dst, "%s\n", subpkt->critical() ? ", critical" : ""); if (dump_packets) { dst_printf(dst, ":subpacket contents:\n"); indent_dest_increase(dst); dst_hexdump(dst, subpkt->data()); indent_dest_decrease(dst); } dump_signature_subpacket(*subpkt); } if (empty) { dst_printf(dst, "none\n"); } } void DumpContextDst::dump_signature_pkt(const pkt::Signature &sig) { indent_dest_increase(dst); dst_printf(dst, "version: %d\n", (int) sig.version); dst_print_sig_type(dst, "type", sig.type()); if (sig.version < PGP_V4) { dst_print_time(dst, "creation time", sig.creation_time); dst_print_keyid(dst, "signing key id", sig.signer); } dst_print_palg(dst, NULL, sig.palg); dst_print_halg(dst, NULL, sig.halg); if (sig.version >= PGP_V4) { dst_printf(dst, "hashed subpackets:\n"); indent_dest_increase(dst); dump_signature_subpackets(sig, true); indent_dest_decrease(dst); dst_printf(dst, "unhashed subpackets:\n"); indent_dest_increase(dst); dump_signature_subpackets(sig, false); indent_dest_decrease(dst); } dst_print_hex(dst, "lbits", sig.lbits.data(), sig.lbits.size(), false); dst_printf(dst, "signature material:\n"); indent_dest_increase(dst); auto material = sig.parse_material(); assert(material); /* LCOV_EXCL_START */ if (!material) { indent_dest_decrease(dst); indent_dest_decrease(dst); return; } /* LCOV_EXCL_END */ switch (sig.palg) { case PGP_PKA_RSA: case PGP_PKA_RSA_ENCRYPT_ONLY: case PGP_PKA_RSA_SIGN_ONLY: { auto &rsa = dynamic_cast(*material); dst_print_mpi(dst, "rsa s", rsa.sig.s, dump_mpi); break; } case PGP_PKA_DSA: { auto &dsa = dynamic_cast(*material); dst_print_mpi(dst, "dsa r", dsa.sig.r, dump_mpi); dst_print_mpi(dst, "dsa s", dsa.sig.s, dump_mpi); break; } case PGP_PKA_EDDSA: case PGP_PKA_ECDSA: case PGP_PKA_SM2: case PGP_PKA_ECDH: { auto &ec = dynamic_cast(*material); dst_print_mpi(dst, "ecc r", ec.sig.r, dump_mpi); dst_print_mpi(dst, "ecc s", ec.sig.s, dump_mpi); break; } /* Wasn't able to find ElGamal sig artifacts so let's ignore this for coverage */ /* LCOV_EXCL_START */ case PGP_PKA_ELGAMAL: case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN: { auto &eg = dynamic_cast(*material); dst_print_mpi(dst, "eg r", eg.sig.r, dump_mpi); dst_print_mpi(dst, "eg s", eg.sig.s, dump_mpi); break; } /* LCOV_EXCL_END */ #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: { auto &ed = dynamic_cast(*material); dst_print_vec(dst, "ed25519 sig", ed.sig.sig, dump_mpi); break; } case PGP_PKA_ED448: { auto &ed = dynamic_cast(*material); dst_print_vec(dst, "ed448 sig", ed.sig.sig, dump_mpi); break; } #endif #if defined(ENABLE_PQC) && defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P384: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P521: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP512: { auto &dilithium = dynamic_cast(*material); dst_print_vec(dst, "mldsa-ecdsa/eddsa sig", dilithium.sig.sig, dump_mpi); break; } case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; case PGP_PKA_SPHINCSPLUS_SHAKE_256s: { auto &slhdsa = dynamic_cast(*material); dst_print_vec(dst, "slhdsa sig", slhdsa.sig.sig, dump_mpi); break; } #endif default: dst_printf(dst, "unknown algorithm\n"); } indent_dest_decrease(dst); indent_dest_decrease(dst); } rnp_result_t DumpContextDst::dump_signature() { dst_printf(dst, "Signature packet\n"); pkt::Signature sig; auto ret = sig.parse(src); if (ret) { indent_dest_increase(dst); dst_printf(dst, "failed to parse\n"); indent_dest_decrease(dst); return ret; } dump_signature_pkt(sig); return RNP_SUCCESS; } void DumpContextDst::dump_key_material(const KeyMaterial *material) { if (!material) { return; } switch (material->alg()) { case PGP_PKA_RSA: case PGP_PKA_RSA_ENCRYPT_ONLY: case PGP_PKA_RSA_SIGN_ONLY: { auto &rsa = dynamic_cast(*material); dst_print_mpi(dst, "rsa n", rsa.n(), dump_mpi); dst_print_mpi(dst, "rsa e", rsa.e(), dump_mpi); return; } case PGP_PKA_DSA: { auto &dsa = dynamic_cast(*material); dst_print_mpi(dst, "dsa p", dsa.p(), dump_mpi); dst_print_mpi(dst, "dsa q", dsa.q(), dump_mpi); dst_print_mpi(dst, "dsa g", dsa.g(), dump_mpi); dst_print_mpi(dst, "dsa y", dsa.y(), dump_mpi); return; } case PGP_PKA_ELGAMAL: case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN: { auto &eg = dynamic_cast(*material); dst_print_mpi(dst, "eg p", eg.p(), dump_mpi); dst_print_mpi(dst, "eg g", eg.g(), dump_mpi); dst_print_mpi(dst, "eg y", eg.y(), dump_mpi); return; } case PGP_PKA_ECDSA: case PGP_PKA_EDDSA: case PGP_PKA_SM2: { auto &ec = dynamic_cast(*material); auto cdesc = ec::Curve::get(ec.curve()); dst_print_mpi(dst, "ecc p", ec.p(), dump_mpi); dst_printf(dst, "ecc curve: %s\n", cdesc ? cdesc->pgp_name : "unknown"); return; } case PGP_PKA_ECDH: { auto &ec = dynamic_cast(*material); auto cdesc = ec::Curve::get(ec.curve()); /* Common EC fields */ dst_print_mpi(dst, "ecdh p", ec.p(), dump_mpi); dst_printf(dst, "ecdh curve: %s\n", cdesc ? cdesc->pgp_name : "unknown"); /* ECDH-only fields */ dst_print_halg(dst, "ecdh hash algorithm", ec.kdf_hash_alg()); dst_printf(dst, "ecdh key wrap algorithm: %d\n", (int) ec.key_wrap_alg()); return; } #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: { auto &ed25519 = dynamic_cast(*material); dst_print_vec(dst, "ed25519", ed25519.pub(), dump_mpi); return; } case PGP_PKA_X25519: { auto &x25519 = dynamic_cast(*material); dst_print_vec(dst, "x25519", x25519.pub(), dump_mpi); return; } case PGP_PKA_ED448: { auto &ed448 = dynamic_cast(*material); dst_print_vec(dst, "ed448", ed448.pub(), dump_mpi); return; } case PGP_PKA_X448: { auto &x448 = dynamic_cast(*material); dst_print_vec(dst, "x448", x448.pub(), dump_mpi); return; } #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: #if defined(ENABLE_PQC) && defined(ENABLE_CRYPTO_REFRESH) FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P384: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P521: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_BP512: #endif { auto &kyber = dynamic_cast(*material); dst_print_vec(dst, "mlkem-ecdh encoded pubkey", kyber.pub().get_encoded(), dump_mpi); return; } #endif #if defined(ENABLE_PQC) && defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P384: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P521: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP512: { auto &dilithium = dynamic_cast(*material); dst_print_vec( dst, "mldsa-ecdsa/eddsa encodced pubkey", dilithium.pub().get_encoded(), dump_mpi); return; } case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; case PGP_PKA_SPHINCSPLUS_SHAKE_256s: { auto &sphincs = dynamic_cast(*material); dst_print_vec(dst, "slhdsa encoded pubkey", sphincs.pub().get_encoded(), dump_mpi); return; } #endif default: dst_printf(dst, "unknown public key algorithm\n"); } } rnp_result_t DumpContextDst::dump_key() { pgp_key_pkt_t key; auto ret = key.parse(src); if (ret) { return ret; } dst_printf(dst, "%s packet\n", id_str_pair::lookup(key_type_map, key.tag, "Unknown")); indent_dest_increase(dst); dst_printf(dst, "version: %d\n", (int) key.version); dst_print_time(dst, "creation time", key.creation_time); if (key.version < PGP_V4) { dst_printf(dst, "v3 validity days: %d\n", (int) key.v3_days); } dst_print_palg(dst, NULL, key.alg); if (key.version == PGP_V5) { dst_printf(dst, "v5 public key material length: %" PRIu32 "\n", key.v5_pub_len); } dst_printf(dst, "public key material:\n"); indent_dest_increase(dst); dump_key_material(key.material.get()); indent_dest_decrease(dst); if (is_secret_key_pkt(key.tag)) { dst_printf(dst, "secret key material:\n"); indent_dest_increase(dst); dst_printf(dst, "s2k usage: %d\n", (int) key.sec_protection.s2k.usage); if (key.version == PGP_V5) { dst_printf(dst, "v5 s2k length: %" PRIu8 "\n", key.v5_s2k_len); } if ((key.sec_protection.s2k.usage == PGP_S2KU_ENCRYPTED) || (key.sec_protection.s2k.usage == PGP_S2KU_ENCRYPTED_AND_HASHED)) { dst_print_salg(dst, NULL, key.sec_protection.symm_alg); dst_print_s2k(dst, key.sec_protection.s2k); if (key.sec_protection.s2k.specifier != PGP_S2KS_EXPERIMENTAL) { size_t bl_size = pgp_block_size(key.sec_protection.symm_alg); if (bl_size) { dst_print_hex(dst, "cipher iv", key.sec_protection.iv, bl_size, true); } else { dst_printf(dst, "cipher iv: unknown algorithm\n"); } } } if (key.version == PGP_V5) { dst_printf(dst, "v5 secret key data length: %" PRIu32 "\n", key.v5_sec_len); } if (!key.sec_protection.s2k.usage) { dst_printf(dst, "cleartext secret key data: %zu bytes\n", key.sec_data.size()); } else { dst_printf(dst, "encrypted secret key data: %zu bytes\n", key.sec_data.size()); } indent_dest_decrease(dst); } try { Fingerprint fp(key); dst_print_keyid(dst, "keyid", fp.keyid()); if (dump_grips) { dst_print_fp(dst, "fingerprint", fp, false); } } catch (const std::exception &e) { dst_printf(dst, "failed to calculate fingerprint and/or keyid\n"); } if (dump_grips) { if (key.material) { KeyGrip grip = key.material->grip(); dst_print_hex(dst, "grip", grip.data(), grip.size(), false); } else { dst_printf(dst, "grip: failed to calculate\n"); } } indent_dest_decrease(dst); return RNP_SUCCESS; } rnp_result_t DumpContextDst::dump_userid() { pgp_userid_pkt_t uid; auto ret = uid.parse(src); if (ret) { return ret; } const char *utype = NULL; switch (uid.tag) { case PGP_PKT_USER_ID: utype = "UserID"; break; case PGP_PKT_USER_ATTR: utype = "UserAttr"; break; default: utype = "Unknown user id"; } dst_printf(dst, "%s packet\n", utype); indent_dest_increase(dst); switch (uid.tag) { case PGP_PKT_USER_ID: dst_printf(dst, "id: "); dst_write(dst, uid.uid); dst_printf(dst, "\n"); break; case PGP_PKT_USER_ATTR: dst_printf(dst, "id: (%zu bytes of data)\n", uid.uid.size()); break; default:; } indent_dest_decrease(dst); return RNP_SUCCESS; } rnp_result_t DumpContextDst::dump_pk_session_key() { pgp_pk_sesskey_t pkey; auto ret = pkey.parse(src); if (ret) { return ret; } auto material = pkey.parse_material(); if (!material) { return RNP_ERROR_BAD_FORMAT; } dst_printf(dst, "Public-key encrypted session key packet\n"); indent_dest_increase(dst); dst_printf(dst, "version: %d\n", (int) pkey.version); #if defined(ENABLE_CRYPTO_REFRESH) if (pkey.version == PGP_PKSK_V6) { dst_print_fp(dst, "fingerprint", pkey.fp); } else { dst_print_keyid(dst, "key id", pkey.key_id); } #else dst_print_keyid(dst, "key id", pkey.key_id); #endif dst_print_palg(dst, NULL, pkey.alg); dst_printf(dst, "encrypted material:\n"); indent_dest_increase(dst); switch (pkey.alg) { case PGP_PKA_RSA: case PGP_PKA_RSA_ENCRYPT_ONLY: case PGP_PKA_RSA_SIGN_ONLY: { auto &rsa = dynamic_cast(*material).enc; dst_print_mpi(dst, "rsa m", rsa.m, dump_mpi); break; } case PGP_PKA_ELGAMAL: case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN: { auto &eg = dynamic_cast(*material).enc; dst_print_mpi(dst, "eg g", eg.g, dump_mpi); dst_print_mpi(dst, "eg m", eg.m, dump_mpi); break; } case PGP_PKA_SM2: { auto &sm2 = dynamic_cast(*material).enc; dst_print_mpi(dst, "sm2 m", sm2.m, dump_mpi); break; } case PGP_PKA_ECDH: { auto &ecdh = dynamic_cast(*material).enc; dst_print_mpi(dst, "ecdh p", ecdh.p, dump_mpi); if (dump_mpi) { dst_print_hex(dst, "ecdh m", ecdh.m.data(), ecdh.m.size(), true); } else { dst_printf(dst, "ecdh m: %zu bytes\n", ecdh.m.size()); } break; } #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_X25519: { auto &x25519 = dynamic_cast(*material).enc; dst_print_vec(dst, "x25519 ephemeral public key", x25519.eph_key, dump_mpi); dst_print_vec(dst, "x25519 encrypted session key", x25519.enc_sess_key, dump_mpi); break; } case PGP_PKA_X448: { auto &x448 = dynamic_cast(*material).enc; dst_print_vec(dst, "x448 ephemeral public key", x448.eph_key, dump_mpi); dst_print_vec(dst, "x448 encrypted session key", x448.enc_sess_key, dump_mpi); break; } #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: #if defined(ENABLE_PQC) && defined(ENABLE_CRYPTO_REFRESH) FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P384: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P521: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_BP512: #endif { auto &mlkem = dynamic_cast(*material).enc; dst_print_vec( dst, "mlkem-ecdh composite ciphertext", mlkem.composite_ciphertext, dump_mpi); dst_print_vec(dst, "mlkem-ecdh wrapped session key", mlkem.wrapped_sesskey, dump_mpi); break; } #endif default: dst_printf(dst, "unknown public key algorithm\n"); } indent_dest_decrease(dst); indent_dest_decrease(dst); return RNP_SUCCESS; } rnp_result_t DumpContextDst::dump_sk_session_key() { pgp_sk_sesskey_t skey; auto ret = skey.parse(src); if (ret) { return ret; } dst_printf(dst, "Symmetric-key encrypted session key packet\n"); indent_dest_increase(dst); dst_printf(dst, "version: %d\n", (int) skey.version); dst_print_salg(dst, NULL, skey.alg); if (skey.version == PGP_SKSK_V5) { dst_print_aalg(dst, NULL, skey.aalg); } dst_print_s2k(dst, skey.s2k); if (skey.version == PGP_SKSK_V5) { dst_print_hex(dst, "aead iv", skey.iv, skey.ivlen, true); } dst_print_hex(dst, "encrypted key", skey.enckey, skey.enckeylen, true); indent_dest_decrease(dst); return RNP_SUCCESS; } rnp_result_t DumpContextDst::dump_aead_encrypted() { dst_printf(dst, "AEAD-encrypted data packet\n"); pgp_aead_hdr_t aead{}; if (!get_aead_hdr(aead)) { dst_printf(dst, "ERROR: failed to read AEAD header\n"); return RNP_ERROR_READ; } indent_dest_increase(dst); dst_printf(dst, "version: %d\n", (int) aead.version); dst_print_salg(dst, NULL, aead.ealg); dst_print_aalg(dst, NULL, aead.aalg); dst_printf(dst, "chunk size: %d\n", (int) aead.csize); dst_print_hex(dst, "initialization vector", aead.iv, aead.ivlen, true); indent_dest_decrease(dst); return RNP_SUCCESS; } rnp_result_t DumpContextDst::dump_encrypted(int tag) { switch (tag) { case PGP_PKT_SE_DATA: dst_printf(dst, "Symmetrically-encrypted data packet\n\n"); break; case PGP_PKT_SE_IP_DATA: dst_printf(dst, "Symmetrically-encrypted integrity protected data packet\n\n"); break; case PGP_PKT_AEAD_ENCRYPTED: return dump_aead_encrypted(); default: dst_printf(dst, "Unknown encrypted data packet\n\n"); break; } return stream_skip_packet(&src); } rnp_result_t DumpContextDst::dump_one_pass() { pgp_one_pass_sig_t onepass; auto ret = onepass.parse(src); if (ret) { return ret; } dst_printf(dst, "One-pass signature packet\n"); indent_dest_increase(dst); dst_printf(dst, "version: %d\n", (int) onepass.version); dst_print_sig_type(dst, NULL, onepass.type); dst_print_halg(dst, NULL, onepass.halg); dst_print_palg(dst, NULL, onepass.palg); #if defined(ENABLE_CRYPTO_REFRESH) if (onepass.version == PGP_OPS_V6) { dst_print_vec(dst, "salt", onepass.salt, false); } #endif if (onepass.version == PGP_OPS_V3) { dst_print_keyid(dst, "signing key id", onepass.keyid); } #if defined(ENABLE_CRYPTO_REFRESH) if (onepass.version == PGP_OPS_V6) { dst_print_fp(dst, NULL, onepass.fp); } #endif dst_printf(dst, "nested: %d\n", (int) onepass.nested); indent_dest_decrease(dst); return RNP_SUCCESS; } rnp_result_t DumpContextDst::dump_compressed() { std::unique_ptr zsrc(new Source()); auto ret = init_compressed_src(&zsrc->src(), &src); if (ret) { return ret; } Source lsrc; if ((ret = init_dump_limited_src(&lsrc.src(), &zsrc->src(), zbudget.get()))) { return ret; } dst_printf(dst, "Compressed data packet\n"); indent_dest_increase(dst); uint8_t zalg = 0; get_compressed_src_alg(&zsrc->src(), &zalg); dst_print_zalg(dst, NULL, (pgp_compression_type_t) zalg); dst_printf(dst, "Decompressed contents:\n"); std::unique_ptr ctx(new DumpContextDst(lsrc.src(), dst)); ctx->copy_params(*this); ret = ctx->dump(true); copy_params(*ctx); if (ret && zbudget->hit) { /* limit on the decompressed data was reached - dump what we have so far */ dst_printf(dst, ":too much decompressed data, stopping.\n"); ret = RNP_SUCCESS; } indent_dest_decrease(dst); return ret; } rnp_result_t DumpContextDst::dump_literal() { Source lsrc; auto ret = init_literal_src(&lsrc.src(), &src); if (ret) { return ret; } dst_printf(dst, "Literal data packet\n"); indent_dest_increase(dst); auto &lhdr = get_literal_src_hdr(lsrc.src()); dst_printf(dst, "data format: '%c'\n", lhdr.format); dst_printf(dst, "filename: %s (len %" PRIu8 ")\n", lhdr.fname, lhdr.fname_len); dst_print_time(dst, "timestamp", lhdr.timestamp); ret = RNP_SUCCESS; while (!lsrc.eof()) { uint8_t readbuf[16384]; size_t read = 0; if (!lsrc.src().read(readbuf, sizeof(readbuf), &read)) { ret = RNP_ERROR_READ; break; } } dst_printf(dst, "data bytes: %zu\n", lsrc.readb()); indent_dest_decrease(dst); return ret; } rnp_result_t DumpContextDst::dump_marker() { dst_printf(dst, "Marker packet\n"); indent_dest_increase(dst); auto ret = stream_parse_marker(src); dst_printf(dst, "contents: %s\n", ret ? "invalid" : PGP_MARKER_CONTENTS); indent_dest_decrease(dst); return ret; } rnp_result_t DumpContextDst::dump_raw_packets() { char msg[1024 + PGP_MAX_HEADER_SIZE] = {0}; char smsg[128] = {0}; rnp_result_t ret = RNP_ERROR_GENERIC; if (src.eof()) { return RNP_SUCCESS; } /* do not allow endless recursion */ if (++layers > MAXIMUM_NESTING_LEVEL) { RNP_LOG("Too many OpenPGP nested layers during the dump."); dst_printf(dst, ":too many OpenPGP packet layers, stopping.\n"); return RNP_SUCCESS; } while (!src.eof()) { if (zbudget->hit) { dst_printf(dst, ":too much decompressed data, stopping.\n"); return RNP_SUCCESS; } if (++dumped_pkts > MAXIMUM_DUMP_PKTS) { RNP_LOG("Too many packets during the dump."); dst_printf(dst, ":too many packets, stopping.\n"); return RNP_SUCCESS; } pgp_packet_hdr_t hdr{}; size_t off = src.readb; rnp_result_t hdrret = stream_peek_packet_hdr(&src, &hdr); if (hdrret) { return hdrret; } if (hdr.partial) { snprintf(msg, sizeof(msg), "partial len"); } else if (hdr.indeterminate) { snprintf(msg, sizeof(msg), "indeterminate len"); } else { snprintf(msg, sizeof(msg), "len %zu", hdr.pkt_len); } vsnprinthex(smsg, sizeof(smsg), hdr.hdr, hdr.hdr_len); dst_printf( dst, ":off %zu: packet header 0x%s (tag %d, %s)\n", off, smsg, hdr.tag, msg); if (dump_packets) { size_t rlen = hdr.pkt_len + hdr.hdr_len; bool part = false; if (!hdr.pkt_len || (rlen > 1024 + hdr.hdr_len)) { rlen = 1024 + hdr.hdr_len; part = true; } dst_printf(dst, ":off %zu: packet contents ", off + hdr.hdr_len); if (!src.peek(msg, rlen, &rlen)) { dst_printf(dst, "- failed to read\n"); } else { rlen -= hdr.hdr_len; if (part || (rlen < hdr.pkt_len)) { dst_printf(dst, "(first %zu bytes)\n", rlen); } else { dst_printf(dst, "(%zu bytes)\n", rlen); } indent_dest_increase(dst); dst_hexdump(dst, (uint8_t *) msg + hdr.hdr_len, rlen); indent_dest_decrease(dst); } dst_printf(dst, "\n"); } switch (hdr.tag) { case PGP_PKT_SIGNATURE: ret = dump_signature(); break; case PGP_PKT_SECRET_KEY: case PGP_PKT_PUBLIC_KEY: case PGP_PKT_SECRET_SUBKEY: case PGP_PKT_PUBLIC_SUBKEY: ret = dump_key(); break; case PGP_PKT_USER_ID: case PGP_PKT_USER_ATTR: ret = dump_userid(); break; case PGP_PKT_PK_SESSION_KEY: ret = dump_pk_session_key(); break; case PGP_PKT_SK_SESSION_KEY: ret = dump_sk_session_key(); break; case PGP_PKT_SE_DATA: case PGP_PKT_SE_IP_DATA: case PGP_PKT_AEAD_ENCRYPTED: stream_pkts++; ret = dump_encrypted(hdr.tag); break; case PGP_PKT_ONE_PASS_SIG: ret = dump_one_pass(); break; case PGP_PKT_COMPRESSED: stream_pkts++; ret = dump_compressed(); break; case PGP_PKT_LITDATA: stream_pkts++; ret = dump_literal(); break; case PGP_PKT_MARKER: ret = dump_marker(); break; case PGP_PKT_TRUST: case PGP_PKT_MDC: dst_printf(dst, "Skipping unhandled pkt: %d\n\n", (int) hdr.tag); ret = stream_skip_packet(&src); break; default: dst_printf(dst, "Skipping Unknown pkt: %d\n\n", (int) hdr.tag); ret = stream_skip_packet(&src); if (ret) { return ret; } if (++failures > MAXIMUM_ERROR_PKTS) { RNP_LOG("too many packet dump errors or unknown packets."); return ret; } } if (ret) { RNP_LOG("failed to process packet"); if (++failures > MAXIMUM_ERROR_PKTS) { RNP_LOG("too many packet dump errors."); return ret; } } if (stream_pkts > MAXIMUM_STREAM_PKTS) { RNP_LOG("Too many OpenPGP stream packets during the dump."); dst_printf(dst, ":too many OpenPGP stream packets, stopping.\n"); return RNP_SUCCESS; } } return RNP_SUCCESS; } rnp_result_t DumpContextDst::dump(bool raw_only) { /* check whether source is cleartext - then skip till the signature */ if (!raw_only && src.is_cleartext()) { dst_printf(dst, ":cleartext signed data\n"); if (!skip_cleartext()) { RNP_LOG("malformed cleartext signed data"); return RNP_ERROR_BAD_FORMAT; } } /* check whether source is armored; concatenated armored messages are all * walked (see issue #2036) */ if (!raw_only && src.is_armored()) { rnp::ArmoredSource armor( src, rnp::ArmoredSource::AllowBinary | rnp::ArmoredSource::AllowMultiple); rnp_result_t ret = RNP_SUCCESS; bool first = true; while (true) { if (armor.eof() && armor.multiple()) { armor.restart(); } if (armor.eof()) { break; } if (first) { dst_printf(dst, ":armored input\n"); first = false; } DumpContextDst ctx(armor.src(), dst); ctx.copy_params(*this); ret = ctx.dump(true); if (ret && !zbudget->hit) { break; } /* accumulate counters, but not layers: each armored message is * dumped at the same nesting level */ stream_pkts = ctx.stream_pkts; failures = ctx.failures; dumped_pkts = ctx.dumped_pkts; if (zbudget->hit) { dst_printf(dst, ":too much decompressed data, stopping.\n"); ret = RNP_SUCCESS; break; } } return ret; } if (src.eof()) { dst_printf(dst, ":empty input\n"); return RNP_SUCCESS; } return dump_raw_packets(); } static bool obj_add_intstr_json(nlohmann::ordered_json &obj, const char * name, int val, const id_str_pair map[]) { if (!rnp::json::add(obj, name, val)) { return false; // LCOV_EXCL_LINE } if (!map) { return true; } char namestr[64] = {0}; const char *str = id_str_pair::lookup(map, val, "Unknown"); snprintf(namestr, sizeof(namestr), "%s.str", name); return rnp::json::add(obj, namestr, str); } static bool obj_add_mpi_json(nlohmann::ordered_json &obj, const char *name, const mpi &mpi, bool contents) { char strname[64] = {0}; snprintf(strname, sizeof(strname), "%s.bits", name); if (!rnp::json::add(obj, strname, (int) mpi.bits())) { return false; // LCOV_EXCL_LINE } if (!contents) { return true; } snprintf(strname, sizeof(strname), "%s.raw", name); return rnp::json::add_hex(obj, strname, mpi.data(), mpi.size()); } static bool subpacket_obj_add_algs(nlohmann::ordered_json & obj, const char * name, const std::vector &algs, const id_str_pair map[]) { auto &jso_algs = obj[name] = nlohmann::ordered_json::array(); for (auto &alg : algs) { jso_algs.push_back((int) alg); } if (!map) { return true; } char strname[64] = {0}; snprintf(strname, sizeof(strname), "%s.str", name); auto &jso_str = obj[strname] = nlohmann::ordered_json::array(); for (auto &alg : algs) { if (!rnp::json::array_add(jso_str, id_str_pair::lookup(map, alg, "Unknown"))) { return false; // LCOV_EXCL_LINE } } return true; } static bool obj_add_s2k_json(nlohmann::ordered_json &obj, pgp_s2k_t *s2k) { auto &s2k_obj = obj["s2k"] = nlohmann::ordered_json::object(); if (!rnp::json::add(s2k_obj, "specifier", (int) s2k->specifier)) { return false; // LCOV_EXCL_LINE } if ((s2k->specifier == PGP_S2KS_EXPERIMENTAL) && s2k->gpg_ext_num) { if (!rnp::json::add(s2k_obj, "gpg extension", (int) s2k->gpg_ext_num)) { return false; // LCOV_EXCL_LINE } if (s2k->gpg_ext_num == PGP_S2K_GPG_SMARTCARD) { size_t slen = s2k->gpg_serial_len > 16 ? 16 : s2k->gpg_serial_len; if (!rnp::json::add_hex(s2k_obj, "card serial number", s2k->gpg_serial, slen)) { return false; // LCOV_EXCL_LINE } } } if (s2k->specifier == PGP_S2KS_EXPERIMENTAL) { return rnp::json::add_hex(s2k_obj, "unknown experimental", s2k->experimental); } if (!obj_add_intstr_json(s2k_obj, "hash algorithm", s2k->hash_alg, hash_alg_map)) { return false; // LCOV_EXCL_LINE } if (((s2k->specifier == PGP_S2KS_SALTED) || (s2k->specifier == PGP_S2KS_ITERATED_AND_SALTED)) && !rnp::json::add_hex(s2k_obj, "salt", s2k->salt, PGP_SALT_SIZE)) { return false; // LCOV_EXCL_LINE } if (s2k->specifier == PGP_S2KS_ITERATED_AND_SALTED) { size_t real_iter = pgp_s2k_decode_iterations(s2k->iterations); if (!rnp::json::add(s2k_obj, "iterations", (uint64_t) real_iter)) { return false; // LCOV_EXCL_LINE } } return true; } bool DumpContextJson::dump_signature_subpacket(const pkt::sigsub::Raw &subpkt, nlohmann::ordered_json &obj) { switch (subpkt.type()) { case pkt::sigsub::Type::CreationTime: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "creation time", (uint64_t) sub.time()); } case pkt::sigsub::Type::ExpirationTime: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "expiration time", (uint64_t) sub.time()); } case pkt::sigsub::Type::ExportableCert: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "exportable", sub.exportable()); } case pkt::sigsub::Type::Trust: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "amount", (int) sub.amount()) && rnp::json::add(obj, "level", (int) sub.level()); } case pkt::sigsub::Type::RegExp: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "regexp", sub.regexp()); } case pkt::sigsub::Type::Revocable: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "revocable", sub.revocable()); } case pkt::sigsub::Type::KeyExpirationTime: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "key expiration", (uint64_t) sub.time()); } case pkt::sigsub::Type::PreferredSymmetric: { auto &sub = dynamic_cast(subpkt); return subpacket_obj_add_algs(obj, "algorithms", sub.algs(), symm_alg_map); } case pkt::sigsub::Type::PreferredHash: { auto &sub = dynamic_cast(subpkt); return subpacket_obj_add_algs(obj, "algorithms", sub.algs(), hash_alg_map); } case pkt::sigsub::Type::PreferredCompress: { auto &sub = dynamic_cast(subpkt); return subpacket_obj_add_algs(obj, "algorithms", sub.algs(), z_alg_map); } case pkt::sigsub::Type::PreferredAEAD: { auto &sub = dynamic_cast(subpkt); return subpacket_obj_add_algs(obj, "algorithms", sub.algs(), aead_alg_map); } case pkt::sigsub::Type::RevocationKey: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "class", (int) sub.rev_class()) && rnp::json::add(obj, "algorithm", (int) sub.alg()) && rnp::json::add(obj, "fingerprint", sub.fp()); } case pkt::sigsub::Type::IssuerKeyID: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "issuer keyid", sub.keyid()); } case pkt::sigsub::Type::KeyserverPrefs: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "no-modify", sub.no_modify()); } case pkt::sigsub::Type::PreferredKeyserver: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "uri", sub.keyserver()); } case pkt::sigsub::Type::PrimaryUserID: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "primary", sub.primary()); } case pkt::sigsub::Type::PolicyURI: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "uri", sub.URI()); } case pkt::sigsub::Type::KeyFlags: { auto & sub = dynamic_cast(subpkt); uint8_t flg = sub.flags(); if (!rnp::json::add(obj, "flags", (int) flg)) { return false; // LCOV_EXCL_LINE } auto &jso_flg = obj["flags.str"] = nlohmann::ordered_json::array(); if ((flg & PGP_KF_CERTIFY) && !rnp::json::array_add(jso_flg, "certify")) { return false; // LCOV_EXCL_LINE } if ((flg & PGP_KF_SIGN) && !rnp::json::array_add(jso_flg, "sign")) { return false; // LCOV_EXCL_LINE } if ((flg & PGP_KF_ENCRYPT_COMMS) && !rnp::json::array_add(jso_flg, "encrypt_comm")) { return false; // LCOV_EXCL_LINE } if ((flg & PGP_KF_ENCRYPT_STORAGE) && !rnp::json::array_add(jso_flg, "encrypt_storage")) { return false; // LCOV_EXCL_LINE } if ((flg & PGP_KF_SPLIT) && !rnp::json::array_add(jso_flg, "split")) { return false; // LCOV_EXCL_LINE } if ((flg & PGP_KF_AUTH) && !rnp::json::array_add(jso_flg, "auth")) { return false; // LCOV_EXCL_LINE } if ((flg & PGP_KF_SHARED) && !rnp::json::array_add(jso_flg, "shared")) { return false; // LCOV_EXCL_LINE } return true; } case pkt::sigsub::Type::SignersUserID: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "uid", sub.signer()); } case pkt::sigsub::Type::RevocationReason: { auto &sub = dynamic_cast(subpkt); if (!obj_add_intstr_json(obj, "code", sub.code(), revoc_reason_map)) { return false; } return rnp::json::add(obj, "message", sub.reason()); } case pkt::sigsub::Type::Features: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "mdc", (bool) (sub.features() & PGP_KEY_FEATURE_MDC)) && rnp::json::add(obj, "aead", (bool) (sub.features() & PGP_KEY_FEATURE_AEAD)) && rnp::json::add(obj, "v5 keys", (bool) (sub.features() & PGP_KEY_FEATURE_V5)); } case pkt::sigsub::Type::EmbeddedSignature: { auto &sub = dynamic_cast(subpkt); if (!sub.signature()) { return false; // LCOV_EXCL_LINE } auto &sig = obj["signature"] = nlohmann::ordered_json::object(); return !dump_signature_pkt(*sub.signature(), sig); } case pkt::sigsub::Type::IssuerFingerprint: { auto &sub = dynamic_cast(subpkt); return rnp::json::add(obj, "fingerprint", sub.fp()); } case pkt::sigsub::Type::NotationData: { auto &sub = dynamic_cast(subpkt); if (!rnp::json::add(obj, "human", sub.human_readable()) || !rnp::json::add(obj, "name", sub.name())) { return false; // LCOV_EXCL_LINE } if (sub.human_readable()) { return rnp::json::add( obj, "value", (char *) sub.value().data(), sub.value().size()); } return rnp::json::add_hex(obj, "value", sub.value()); } default: if (!dump_packets) { return rnp::json::add_hex(obj, "raw", subpkt.data()); } return true; } return true; } nlohmann::ordered_json DumpContextJson::dump_signature_subpackets(const pkt::Signature &sig) { nlohmann::ordered_json res = nlohmann::ordered_json::array(); for (auto &subpkt : sig.subpkts) { auto &jso_subpkt = res.emplace_back(nlohmann::ordered_json::object()); if (!obj_add_intstr_json( jso_subpkt, "type", subpkt->raw_type(), sig_subpkt_type_map)) { return nlohmann::ordered_json(); // LCOV_EXCL_LINE } if (!rnp::json::add(jso_subpkt, "length", (int) subpkt->data().size())) { return nlohmann::ordered_json(); // LCOV_EXCL_LINE } if (!rnp::json::add(jso_subpkt, "hashed", subpkt->hashed())) { return nlohmann::ordered_json(); // LCOV_EXCL_LINE } if (!rnp::json::add(jso_subpkt, "critical", subpkt->critical())) { return nlohmann::ordered_json(); // LCOV_EXCL_LINE } if (dump_packets && !rnp::json::add_hex(jso_subpkt, "raw", subpkt->data())) { return nlohmann::ordered_json(); // LCOV_EXCL_LINE } if (!dump_signature_subpacket(*subpkt, jso_subpkt)) { return nlohmann::ordered_json(); } } return res; } rnp_result_t DumpContextJson::dump_signature_pkt(const pkt::Signature &sig, nlohmann::ordered_json &pkt) { if (!rnp::json::add(pkt, "version", (int) sig.version)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (!obj_add_intstr_json(pkt, "type", sig.type(), sig_type_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (sig.version < PGP_V4) { if (!rnp::json::add(pkt, "creation time", (uint64_t) sig.creation_time)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (!rnp::json::add(pkt, "signer", sig.signer)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } } if (!obj_add_intstr_json(pkt, "algorithm", sig.palg, pubkey_alg_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (!obj_add_intstr_json(pkt, "hash algorithm", sig.halg, hash_alg_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (sig.version >= PGP_V4) { nlohmann::ordered_json subpkts = dump_signature_subpackets(sig); if (subpkts.is_null()) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } pkt["subpackets"] = std::move(subpkts); } if (!rnp::json::add_hex(pkt, "lbits", sig.lbits.data(), sig.lbits.size())) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } auto &material = pkt["material"] = nlohmann::ordered_json::object(); auto sigmaterial = sig.parse_material(); if (!sigmaterial) { return RNP_ERROR_BAD_PARAMETERS; } switch (sig.palg) { case PGP_PKA_RSA: case PGP_PKA_RSA_ENCRYPT_ONLY: case PGP_PKA_RSA_SIGN_ONLY: { auto &rsa = dynamic_cast(*sigmaterial); if (!obj_add_mpi_json(material, "s", rsa.sig.s, dump_mpi)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } break; } case PGP_PKA_DSA: { auto &dsa = dynamic_cast(*sigmaterial); if (!obj_add_mpi_json(material, "r", dsa.sig.r, dump_mpi) || !obj_add_mpi_json(material, "s", dsa.sig.s, dump_mpi)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } break; } case PGP_PKA_EDDSA: case PGP_PKA_ECDSA: case PGP_PKA_SM2: case PGP_PKA_ECDH: { auto &ec = dynamic_cast(*sigmaterial); if (!obj_add_mpi_json(material, "r", ec.sig.r, dump_mpi) || !obj_add_mpi_json(material, "s", ec.sig.s, dump_mpi)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } break; } /* Wasn't able to find ElGamal sig artifacts so let's ignore this for coverage */ /* LCOV_EXCL_START */ case PGP_PKA_ELGAMAL: case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN: { auto &eg = dynamic_cast(*sigmaterial); if (!obj_add_mpi_json(material, "r", eg.sig.r, dump_mpi) || !obj_add_mpi_json(material, "s", eg.sig.s, dump_mpi)) { return RNP_ERROR_OUT_OF_MEMORY; } break; } /* LCOV_EXCL_END */ #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: case PGP_PKA_ED448: /* TODO */ break; #endif #if defined(ENABLE_PQC) && defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P384: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P521: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP512: /* TODO */ break; case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; case PGP_PKA_SPHINCSPLUS_SHAKE_256s: /* TODO */ break; #endif default: break; } return RNP_SUCCESS; } rnp_result_t DumpContextJson::dump_signature(nlohmann::ordered_json &pkt) { pkt::Signature sig; auto ret = sig.parse(src); if (ret) { return ret; } return dump_signature_pkt(sig, pkt); } bool DumpContextJson::dump_key_material(const KeyMaterial *material, nlohmann::ordered_json &jso) { if (!material) { return false; // LCOV_EXCL_LINE } switch (material->alg()) { case PGP_PKA_RSA: case PGP_PKA_RSA_ENCRYPT_ONLY: case PGP_PKA_RSA_SIGN_ONLY: { auto &rsa = dynamic_cast(*material); if (!obj_add_mpi_json(jso, "n", rsa.n(), dump_mpi) || !obj_add_mpi_json(jso, "e", rsa.e(), dump_mpi)) { return false; // LCOV_EXCL_LINE } return true; } case PGP_PKA_DSA: { auto &dsa = dynamic_cast(*material); if (!obj_add_mpi_json(jso, "p", dsa.p(), dump_mpi) || !obj_add_mpi_json(jso, "q", dsa.q(), dump_mpi) || !obj_add_mpi_json(jso, "g", dsa.g(), dump_mpi) || !obj_add_mpi_json(jso, "y", dsa.y(), dump_mpi)) { return false; // LCOV_EXCL_LINE } return true; } case PGP_PKA_ELGAMAL: case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN: { auto &eg = dynamic_cast(*material); if (!obj_add_mpi_json(jso, "p", eg.p(), dump_mpi) || !obj_add_mpi_json(jso, "g", eg.g(), dump_mpi) || !obj_add_mpi_json(jso, "y", eg.y(), dump_mpi)) { return false; // LCOV_EXCL_LINE } return true; } case PGP_PKA_ECDSA: case PGP_PKA_EDDSA: case PGP_PKA_SM2: case PGP_PKA_ECDH: { auto &ec = dynamic_cast(*material); auto cdesc = ec::Curve::get(ec.curve()); /* Common EC fields */ if (!obj_add_mpi_json(jso, "p", ec.p(), dump_mpi)) { return false; // LCOV_EXCL_LINE } if (!rnp::json::add(jso, "curve", cdesc ? cdesc->pgp_name : "unknown")) { return false; // LCOV_EXCL_LINE } if (material->alg() != PGP_PKA_ECDH) { return true; } /* ECDH-only fields */ auto &ecdh = dynamic_cast(*material); if (!obj_add_intstr_json(jso, "hash algorithm", ecdh.kdf_hash_alg(), hash_alg_map)) { return false; // LCOV_EXCL_LINE } if (!obj_add_intstr_json( jso, "key wrap algorithm", ecdh.key_wrap_alg(), symm_alg_map)) { return false; // LCOV_EXCL_LINE } return true; } #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: case PGP_PKA_X25519: case PGP_PKA_ED448: case PGP_PKA_X448: /* TODO */ return true; #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: #if defined(ENABLE_PQC) && defined(ENABLE_CRYPTO_REFRESH) FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P384: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P521: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_BP512: // TODO #endif return true; #endif #if defined(ENABLE_PQC) && defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_DILITHIUM3_ED25519: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_ED448: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_P384: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_P521: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM3_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_DILITHIUM5_BP512: /* TODO */ return true; case PGP_PKA_SPHINCSPLUS_SHAKE_128f: FALLTHROUGH_STATEMENT; case PGP_PKA_SPHINCSPLUS_SHAKE_128s: FALLTHROUGH_STATEMENT; case PGP_PKA_SPHINCSPLUS_SHAKE_256s: /* TODO */ return true; #endif default: return false; } } rnp_result_t DumpContextJson::dump_key(nlohmann::ordered_json &pkt) { pgp_key_pkt_t key; auto ret = key.parse(src); if (ret) { return ret; } if (!rnp::json::add(pkt, "version", (int) key.version)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (!rnp::json::add(pkt, "creation time", (uint64_t) key.creation_time)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if ((key.version < PGP_V4) && !rnp::json::add(pkt, "v3 days", (int) key.v3_days)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (!obj_add_intstr_json(pkt, "algorithm", key.alg, pubkey_alg_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if ((key.version == PGP_V5) && !rnp::json::add(pkt, "v5 public key material length", (int) key.v5_pub_len)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } auto &material = pkt["material"] = nlohmann::ordered_json::object(); if (!dump_key_material(key.material.get(), material)) { return RNP_ERROR_OUT_OF_MEMORY; } if (is_secret_key_pkt(key.tag)) { if (!rnp::json::add(material, "s2k usage", (int) key.sec_protection.s2k.usage)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if ((key.version == PGP_V5) && !rnp::json::add(material, "v5 s2k length", (int) key.v5_s2k_len)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (!obj_add_s2k_json(material, &key.sec_protection.s2k)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (key.sec_protection.s2k.usage && !obj_add_intstr_json( material, "symmetric algorithm", key.sec_protection.symm_alg, symm_alg_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if ((key.version == PGP_V5) && !rnp::json::add(material, "v5 secret key data length", (int) key.v5_sec_len)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } } Fingerprint fp(key); if (!rnp::json::add(pkt, "keyid", fp.keyid())) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (dump_grips && !rnp::json::add(pkt, "fingerprint", fp)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (dump_grips) { if (key.material) { KeyGrip grip = key.material->grip(); if (!rnp::json::add_hex(pkt, "grip", grip.data(), grip.size())) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } } else { return RNP_ERROR_BAD_PARAMETERS; // LCOV_EXCL_LINE } } return RNP_SUCCESS; } rnp_result_t DumpContextJson::dump_user_id(nlohmann::ordered_json &pkt) { pgp_userid_pkt_t uid; auto ret = uid.parse(src); if (ret) { return ret; } switch (uid.tag) { case PGP_PKT_USER_ID: if (!rnp::json::add(pkt, "userid", (char *) uid.uid.data(), uid.uid.size())) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } break; case PGP_PKT_USER_ATTR: if (!rnp::json::add_hex(pkt, "userattr", uid.uid.data(), uid.uid.size())) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } break; default:; } return RNP_SUCCESS; } rnp_result_t DumpContextJson::dump_pk_session_key(nlohmann::ordered_json &pkt) { pgp_pk_sesskey_t pkey; auto ret = pkey.parse(src); if (ret) { return ret; } auto pkmaterial = pkey.parse_material(); if (!pkmaterial) { return RNP_ERROR_BAD_FORMAT; } if (!rnp::json::add(pkt, "version", (int) pkey.version) || !rnp::json::add(pkt, "keyid", pkey.key_id) || !obj_add_intstr_json(pkt, "algorithm", pkey.alg, pubkey_alg_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } auto &material = pkt["material"] = nlohmann::ordered_json::object(); switch (pkey.alg) { case PGP_PKA_RSA: case PGP_PKA_RSA_ENCRYPT_ONLY: case PGP_PKA_RSA_SIGN_ONLY: { auto &rsa = dynamic_cast(*pkmaterial).enc; if (!obj_add_mpi_json(material, "m", rsa.m, dump_mpi)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } break; } case PGP_PKA_ELGAMAL: case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN: { auto &eg = dynamic_cast(*pkmaterial).enc; if (!obj_add_mpi_json(material, "g", eg.g, dump_mpi) || !obj_add_mpi_json(material, "m", eg.m, dump_mpi)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } break; } case PGP_PKA_SM2: { auto &sm2 = dynamic_cast(*pkmaterial).enc; if (!obj_add_mpi_json(material, "m", sm2.m, dump_mpi)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } break; } case PGP_PKA_ECDH: { auto &ecdh = dynamic_cast(*pkmaterial).enc; if (!obj_add_mpi_json(material, "p", ecdh.p, dump_mpi) || !rnp::json::add(material, "m.bytes", (int) ecdh.m.size())) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (dump_mpi && !rnp::json::add_hex(material, "m", ecdh.m.data(), ecdh.m.size())) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } break; } #if defined(ENABLE_CRYPTO_REFRESH) case PGP_PKA_ED25519: case PGP_PKA_X25519: case PGP_PKA_ED448: case PGP_PKA_X448: /* TODO */ break; #endif #if defined(ENABLE_PQC) case PGP_PKA_KYBER768_X25519: #if defined(ENABLE_PQC) && defined(ENABLE_CRYPTO_REFRESH) FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_X448: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_P384: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_P521: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER768_BP384: FALLTHROUGH_STATEMENT; case PGP_PKA_KYBER1024_BP512: // TODO #endif break; #endif default:; } return RNP_SUCCESS; } rnp_result_t DumpContextJson::dump_sk_session_key(nlohmann::ordered_json &pkt) { pgp_sk_sesskey_t skey; auto ret = skey.parse(src); if (ret) { return ret; } if (!rnp::json::add(pkt, "version", (int) skey.version) || !obj_add_intstr_json(pkt, "algorithm", skey.alg, symm_alg_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if ((skey.version == PGP_SKSK_V5) && !obj_add_intstr_json(pkt, "aead algorithm", skey.aalg, aead_alg_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (!obj_add_s2k_json(pkt, &skey.s2k)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if ((skey.version == PGP_SKSK_V5) && !rnp::json::add_hex(pkt, "aead iv", skey.iv, skey.ivlen)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (!rnp::json::add_hex(pkt, "encrypted key", skey.enckey, skey.enckeylen)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } return RNP_SUCCESS; } rnp_result_t DumpContextJson::dump_encrypted(nlohmann::ordered_json &pkt, pgp_pkt_type_t tag) { if (tag != PGP_PKT_AEAD_ENCRYPTED) { /* packet header with tag is already in pkt */ return stream_skip_packet(&src); } /* dumping AEAD data */ pgp_aead_hdr_t aead{}; if (!get_aead_hdr(aead)) { return RNP_ERROR_READ; } if (!rnp::json::add(pkt, "version", (int) aead.version) || !obj_add_intstr_json(pkt, "algorithm", aead.ealg, symm_alg_map) || !obj_add_intstr_json(pkt, "aead algorithm", aead.aalg, aead_alg_map) || !rnp::json::add(pkt, "chunk size", (int) aead.csize) || !rnp::json::add_hex(pkt, "aead iv", aead.iv, aead.ivlen)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } return RNP_SUCCESS; } rnp_result_t DumpContextJson::dump_one_pass(nlohmann::ordered_json &pkt) { pgp_one_pass_sig_t onepass; auto ret = onepass.parse(src); if (ret) { return ret; } if (!rnp::json::add(pkt, "version", (int) onepass.version)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (!obj_add_intstr_json(pkt, "type", onepass.type, sig_type_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (!obj_add_intstr_json(pkt, "hash algorithm", onepass.halg, hash_alg_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } if (!obj_add_intstr_json(pkt, "public key algorithm", onepass.palg, pubkey_alg_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } #if defined(ENABLE_CRYPTO_REFRESH) if (onepass.version == PGP_OPS_V6 && !rnp::json::add( pkt, "salt", (const char *) onepass.salt.data(), onepass.salt.size())) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } #endif if (onepass.version == PGP_OPS_V3 && !rnp::json::add(pkt, "signer", onepass.keyid)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } #if defined(ENABLE_CRYPTO_REFRESH) if (onepass.version == PGP_OPS_V6 && !rnp::json::add(pkt, "signer", onepass.fp)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } #endif if (!rnp::json::add(pkt, "nested", (bool) onepass.nested)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } return RNP_SUCCESS; } rnp_result_t DumpContextJson::dump_marker(nlohmann::ordered_json &pkt) { auto ret = stream_parse_marker(src); if (!rnp::json::add(pkt, "contents", ret ? "invalid" : PGP_MARKER_CONTENTS)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } return ret; } rnp_result_t DumpContextJson::dump_compressed(nlohmann::ordered_json &pkt) { std::unique_ptr zsrc(new Source()); auto ret = init_compressed_src(&zsrc->src(), &src); if (ret) { return ret; } Source lsrc; if ((ret = init_dump_limited_src(&lsrc.src(), &zsrc->src(), zbudget.get()))) { return ret; } uint8_t zalg; get_compressed_src_alg(&zsrc->src(), &zalg); if (!obj_add_intstr_json(pkt, "algorithm", zalg, z_alg_map)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } nlohmann::ordered_json contents; DumpContextJson ctx(lsrc.src(), &contents); ctx.copy_params(*this); ret = ctx.dump(true); copy_params(ctx); if (zbudget->hit) { /* limit on the decompressed data was reached - dump what we have so far */ pkt["contents"] = std::move(contents); return RNP_SUCCESS; } if (!ret) { pkt["contents"] = std::move(contents); } return ret; } rnp_result_t DumpContextJson::dump_literal(nlohmann::ordered_json &pkt) { Source lsrc; auto ret = init_literal_src(&lsrc.src(), &src); if (ret) { return ret; } ret = RNP_ERROR_OUT_OF_MEMORY; auto &lhdr = get_literal_src_hdr(lsrc.src()); if (!rnp::json::add(pkt, "format", (char *) &lhdr.format, 1) || !rnp::json::add(pkt, "filename", (char *) lhdr.fname, lhdr.fname_len) || !rnp::json::add(pkt, "timestamp", (uint64_t) lhdr.timestamp)) { return ret; // LCOV_EXCL_LINE } while (!lsrc.eof()) { uint8_t readbuf[16384]; size_t read = 0; if (!lsrc.src().read(readbuf, sizeof(readbuf), &read)) { return RNP_ERROR_READ; } } if (!rnp::json::add(pkt, "datalen", (uint64_t) lsrc.readb())) { return ret; // LCOV_EXCL_LINE } return RNP_SUCCESS; } bool DumpContextJson::dump_pkt_hdr(pgp_packet_hdr_t &hdr, nlohmann::ordered_json &pkt) { auto hdrret = stream_peek_packet_hdr(&src, &hdr); if (hdrret) { return false; } auto &jso_hdr = pkt["header"] = nlohmann::ordered_json::object(); if (!rnp::json::add(jso_hdr, "offset", (uint64_t) src.readb) || !obj_add_intstr_json(jso_hdr, "tag", hdr.tag, packet_tag_map) || !rnp::json::add_hex(jso_hdr, "raw", hdr.hdr, hdr.hdr_len)) { return false; // LCOV_EXCL_LINE } if (!hdr.partial && !hdr.indeterminate && !rnp::json::add(jso_hdr, "length", (uint64_t) hdr.pkt_len)) { return false; // LCOV_EXCL_LINE } if (!rnp::json::add(jso_hdr, "partial", hdr.partial) || !rnp::json::add(jso_hdr, "indeterminate", hdr.indeterminate)) { return false; // LCOV_EXCL_LINE } return true; } rnp_result_t DumpContextJson::dump_raw_packets() { rnp_result_t ret = RNP_ERROR_GENERIC; nlohmann::ordered_json pkts = nlohmann::ordered_json::array(); if (src.eof()) { *json = std::move(pkts); return RNP_SUCCESS; } /* do not allow endless recursion */ if (++layers > MAXIMUM_NESTING_LEVEL) { RNP_LOG("Too many OpenPGP nested layers during the dump."); *json = std::move(pkts); return RNP_SUCCESS; } while (!src.eof()) { if (zbudget->hit) { break; } if (++dumped_pkts > MAXIMUM_DUMP_PKTS) { RNP_LOG("Too many packets during the dump."); break; } auto & pkt = pkts.emplace_back(nlohmann::ordered_json::object()); pgp_packet_hdr_t hdr{}; if (!dump_pkt_hdr(hdr, pkt)) { return RNP_ERROR_OUT_OF_MEMORY; } if (dump_packets) { size_t rlen = hdr.pkt_len + hdr.hdr_len; uint8_t buf[2048 + sizeof(hdr.hdr)] = {0}; if (!hdr.pkt_len || (rlen > 2048 + hdr.hdr_len)) { rlen = 2048 + hdr.hdr_len; } if (!src.peek(buf, rlen, &rlen) || (rlen < hdr.hdr_len)) { return RNP_ERROR_READ; } if (!rnp::json::add_hex(pkt, "raw", buf + hdr.hdr_len, rlen - hdr.hdr_len)) { return RNP_ERROR_OUT_OF_MEMORY; // LCOV_EXCL_LINE } } switch (hdr.tag) { case PGP_PKT_SIGNATURE: ret = dump_signature(pkt); break; case PGP_PKT_SECRET_KEY: case PGP_PKT_PUBLIC_KEY: case PGP_PKT_SECRET_SUBKEY: case PGP_PKT_PUBLIC_SUBKEY: ret = dump_key(pkt); break; case PGP_PKT_USER_ID: case PGP_PKT_USER_ATTR: ret = dump_user_id(pkt); break; case PGP_PKT_PK_SESSION_KEY: ret = dump_pk_session_key(pkt); break; case PGP_PKT_SK_SESSION_KEY: ret = dump_sk_session_key(pkt); break; case PGP_PKT_SE_DATA: case PGP_PKT_SE_IP_DATA: case PGP_PKT_AEAD_ENCRYPTED: stream_pkts++; ret = dump_encrypted(pkt, hdr.tag); break; case PGP_PKT_ONE_PASS_SIG: ret = dump_one_pass(pkt); break; case PGP_PKT_COMPRESSED: stream_pkts++; ret = dump_compressed(pkt); break; case PGP_PKT_LITDATA: stream_pkts++; ret = dump_literal(pkt); break; case PGP_PKT_MARKER: ret = dump_marker(pkt); break; case PGP_PKT_TRUST: case PGP_PKT_MDC: ret = stream_skip_packet(&src); break; default: ret = stream_skip_packet(&src); if (ret) { return ret; } if (++failures > MAXIMUM_ERROR_PKTS) { RNP_LOG("too many packet dump errors or unknown packets."); return RNP_ERROR_BAD_FORMAT; } } if (ret) { RNP_LOG("failed to process packet"); if (++failures > MAXIMUM_ERROR_PKTS) { RNP_LOG("too many packet dump errors."); return ret; } } if (stream_pkts > MAXIMUM_STREAM_PKTS) { RNP_LOG("Too many OpenPGP stream packets during the dump."); break; } } *json = std::move(pkts); return RNP_SUCCESS; } rnp_result_t DumpContextJson::dump(bool raw_only) { /* check whether source is cleartext - then skip till the signature */ if (!raw_only && src.is_cleartext()) { if (!skip_cleartext()) { RNP_LOG("malformed cleartext signed data"); return RNP_ERROR_BAD_FORMAT; } } /* check whether source is armored; concatenated armored messages are * walked and all of their packets collected into a single array (#2036) */ if (!raw_only && src.is_armored()) { rnp::ArmoredSource armor( src, rnp::ArmoredSource::AllowBinary | rnp::ArmoredSource::AllowMultiple); rnp_result_t ret = RNP_SUCCESS; nlohmann::ordered_json res = nlohmann::ordered_json::array(); while (true) { if (armor.eof() && armor.multiple()) { armor.restart(); } if (armor.eof()) { break; } nlohmann::ordered_json block; DumpContextJson ctx(armor.src(), &block); ctx.copy_params(*this); ret = ctx.dump(true); if (ret && !zbudget->hit) { break; } /* accumulate counters, but not layers: each armored message is * dumped at the same nesting level */ stream_pkts = ctx.stream_pkts; failures = ctx.failures; dumped_pkts = ctx.dumped_pkts; if (block.is_array()) { res.insert(res.end(), block.begin(), block.end()); } if (zbudget->hit) { ret = RNP_SUCCESS; break; } } *json = std::move(res); return ret; } if (src.eof()) { return RNP_ERROR_NOT_ENOUGH_DATA; } return dump_raw_packets(); } } // namespace rnp